diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..c8691c4e3906578092e44e11fd703235751ac858 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.data \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2c492becb5bd60b5cf4494429512a4d233880b1b..dd7df3c27fb6133f95f9ff88f16e3d598a68019f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,7 +22,7 @@ DRF Unit Tests: - export DEBUG=True - pip install -r requirements-dev.txt script: - - coverage run --include='main/*,stok_darah/*,donor/*,rest_framework_authlib/*,acara_donor/*' manage.py test + - coverage run --include='main/*,stok_darah/*,donor/*,rest_framework_authlib/*,acara_donor/*,edukasi/*' manage.py test - coverage report --show-missing - coverage xml artifacts: diff --git a/backend/.gitignore b/backend/.gitignore index 80861d93e170db86087acb5f22efc2e1e0a75840..ff597a63fff53a874e8f7603318960d5c63a82fd 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -10,3 +10,4 @@ db.sqlite3 .coverage coverage.xml .idea/ +media/ \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index fd3d4561a5ebc95ff1cc23f27b0973311cd8ff9d..2ba87ad96f707d1abd51911c349e919462881132 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -28,4 +28,6 @@ ENV DJANGO_SETTINGS_MODULE 'dblood.settings.production' ENV DEBUG 'False' ENV STATIC_URL '/api/static/' +RUN ["python", "manage.py", "installtasks"] + CMD ["gunicorn", "dblood.wsgi", "--bind", "0.0.0.0:8000"] diff --git a/backend/README.md b/backend/README.md index e3a1be18c67ae52c30f4f4cd1340fed6a533836c..9edd5329cbbc50c4316c7c07c07bcbfffad4f897 100644 --- a/backend/README.md +++ b/backend/README.md @@ -47,7 +47,7 @@ PS C:\Users\Giovan\Projects\mantan-aab-d-blood\drf> Set-ExecutionPolicy -Scope C 5. Run unit tests and measure coverage ``` -(venv) PS C:\Users\Giovan\Projects\mantan-aab-d-blood\drf> coverage run --include='main/*,stok_darah/*,donor/*,rest_framework_authlib/*' manage.py test +(venv) PS C:\Users\Giovan\Projects\mantan-aab-d-blood\drf> coverage run --include='main/*,stok_darah/*,donor/*,rest_framework_authlib/*,acara_donor/*,edukasi/*' manage.py test (venv) PS C:\Users\Giovan\Projects\mantan-aab-d-blood\drf> coverage report --show-missing ``` diff --git a/backend/acara_donor/admin.py b/backend/acara_donor/admin.py index e400381c93c02f0a196e5337bf0650f2e5e7bd5a..d583317f1ead724551ad46643c2d3327094582b6 100644 --- a/backend/acara_donor/admin.py +++ b/backend/acara_donor/admin.py @@ -1,17 +1,82 @@ from django.contrib import admin +from django.conf import settings +from django.utils.timezone import localtime +from django.utils.html import mark_safe from acara_donor.models import AcaraDonor +from donor.models import JadwalDonor from acara_donor.filters import AcaraDonorFilter @admin.register(AcaraDonor) class AcaraDonorAdmin(admin.ModelAdmin): - list_filter = ('status', AcaraDonorFilter, 'alamat_lokasi_donor') - readonly_fields = ('nomor', + list_display = ('nama_institusi', 'alamat_lokasi_donor', + 'display_waktu', 'status') + list_filter = ('status', AcaraDonorFilter, 'kecamatan', 'kategori') + search_fields = ('waktu_mulai', 'alamat_lokasi_donor') + + readonly_fields = ('id', 'user', 'nama_institusi', 'alamat_institusi', - 'no_telp_kantor', 'email_kantor', + 'no_telp_kantor', 'nama_koor', 'email_koor', - 'no_telp_koor') + 'no_telp_koor', + 'keterangan', + 'foto_lokasi', + 'show_image') + fieldsets = ( + (None, { + 'fields': ('id', + 'user', + 'status') + }), + ('INFORMASI ACARA DONOR', { + 'fields': ('kategori', + 'alamat_lokasi_donor', + 'kecamatan', + ('waktu_mulai', 'waktu_berakhir'), + 'perkiraan_jumlah_donor', + 'keterangan', + 'show_image') + }), + ('INFORMASI PENYELENGGARA', { + 'fields': ('nama_institusi', 'alamat_institusi', + 'email_kantor', 'no_telp_kantor') + }), + ('KONTAK KOORDINATOR', { + 'fields': ('nama_koor', 'email_koor', 'no_telp_koor') + }), + ) + + def display_waktu(self, obj): + """Create a string for waktu acara donor.""" + waktu_mulai = localtime(obj.waktu_mulai) + waktu_berakhir = localtime(obj.waktu_berakhir) + return f"{waktu_mulai.date().strftime('%d/%m/%Y')} \ + ({waktu_mulai.hour:02d}:{waktu_mulai.minute:02d} \ + - {waktu_berakhir.hour:02d}:{waktu_berakhir.minute:02d})" + + def show_image(self, obj): + """Create an image preview for foto lokasi acara donor.""" + return mark_safe('' + % (obj.foto_lokasi.url)) + + display_waktu.short_description = 'Waktu' + show_image.short_description = 'Preview foto lokasi' + + class Media: + js = [settings.MEDIA_URL+'/js/acara_donor_custom_admin.js', ] + + def save_model(self, request, obj, form, change): + if(obj.status): + JadwalDonor.objects.create(kecamatan=obj.kecamatan, + location=obj.alamat_lokasi_donor, + time_start=obj.waktu_mulai, + time_end=obj.waktu_berakhir, + quota=obj.perkiraan_jumlah_donor, + category=obj.kategori) + + super().save_model(request, obj, form, change) diff --git a/backend/acara_donor/factories.py b/backend/acara_donor/factories.py index 05d6cfc776b317a18b679c34a4a3dcfcd6331e1d..996e2d4a4b0482b09b5588571f1a396f2aae08af 100644 --- a/backend/acara_donor/factories.py +++ b/backend/acara_donor/factories.py @@ -1,6 +1,9 @@ import factory -import dateutil.tz +from datetime import timedelta +from django.utils import timezone +from random import choice, randint from acara_donor.models import AcaraDonor +from donor.models import JadwalDonor from main.factories import UserFactory @@ -11,17 +14,27 @@ class AcaraDonorFactory(factory.DjangoModelFactory): class Meta: model = AcaraDonor - nomor = factory.Faker('uuid4') user = factory.SubFactory(UserFactory) status = factory.Faker('boolean', chance_of_getting_true=66) + nama_institusi = factory.Faker('company') alamat_institusi = factory.Faker('address', locale=LOCALE) - alamat_lokasi_donor = factory.Faker('address', locale=LOCALE) - no_telp_kantor = factory.Faker('phone_number') email_kantor = factory.Faker('company_email', locale=LOCALE) + no_telp_kantor = factory.Sequence(lambda n: '+6281-1128-%04d' % n) + nama_koor = factory.Faker('name', locale=LOCALE) email_koor = factory.Faker('free_email', locale=LOCALE) - no_telp_koor = factory.Faker('phone_number') - waktu_donor = factory.Faker('future_datetime', tzinfo=dateutil.tz.gettz('Asia/Jakarta')) + no_telp_koor = factory.Sequence(lambda n: '021-1128-%04d' % n) + + kategori = factory.LazyAttribute( + lambda _: choice(JadwalDonor.Category.choices)[0]) # NOSONAR + alamat_lokasi_donor = factory.Faker('address', locale=LOCALE) + kecamatan = factory.LazyAttribute( + lambda _: choice(JadwalDonor.Kecamatan.choices)[0]) # NOSONAR + waktu_mulai = factory.LazyAttribute( + lambda _: timezone.now() + timedelta(days=randint(1, 66), # NOSONAR + hours=randint(1, 23))) # NOSONAR + waktu_berakhir = factory.LazyAttribute( + lambda t: t.waktu_mulai + timedelta(hours=randint(2, 6))) # NOSONAR perkiraan_jumlah_donor = factory.fuzzy.FuzzyInteger(low=33, high=666) keterangan = factory.Faker('text') diff --git a/backend/acara_donor/filters.py b/backend/acara_donor/filters.py index 1472b126dcb8333d0c27a3f05f0bda224676d5d1..32c967f448c1487b42c14e045fd1d6489f4bd4e7 100644 --- a/backend/acara_donor/filters.py +++ b/backend/acara_donor/filters.py @@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _ class AcaraDonorFilter(admin.SimpleListFilter): title = _('waktu donor') - parameter_name = 'waktu_donor' + parameter_name = 'waktu_mulai' def lookups(self, request, model_admin): return ( @@ -24,33 +24,34 @@ class AcaraDonorFilter(admin.SimpleListFilter): def __last_day_of_month(self, any_day): next_month = any_day.replace(day=28) + datetime.timedelta(days=4) - return next_month - datetime.timedelta(days=next_month.day) + datetime.timedelta(days=1) + return next_month - datetime.timedelta(days=next_month.day) \ + + datetime.timedelta(days=1) def queryset(self, request, queryset): - today = localtime(now()).replace(hour=0, minute=0, second=0, microsecond=0) - tomorrow = today + datetime.timedelta(days=1) + today = localtime(now()).replace( + hour=0, minute=0, second=0, microsecond=0) next_seven_days = today + datetime.timedelta(days=8) this_month = today.replace(day=1) end_of_this_month = self.__last_day_of_month(today) - start_of_next_month = today.replace(year=today.year, month=today.month + 1, day=1) + start_of_next_month = today.replace(year=today.year, + month=today.month + 1, day=1) end_of_next_month = self.__last_day_of_month(start_of_next_month) - this_year = today.replace(month=1, day=1) - next_year = today.replace(year=today.year + 1, month=1, day=1) - if self.value() == 'today': - return queryset.filter(waktu_donor__range=[today, tomorrow]) + return queryset.filter(waktu_mulai__date=today) if self.value() == 'next_seven_days': - return queryset.filter(waktu_donor__range=[today, next_seven_days]) + return queryset.filter(waktu_mulai__range=[today, next_seven_days]) if self.value() == 'this_month': - return queryset.filter(waktu_donor__range=[this_month, end_of_this_month]) + return queryset.filter( + waktu_mulai__range=[this_month, end_of_this_month]) if self.value() == 'next_month': - return queryset.filter(waktu_donor__range=[start_of_next_month, end_of_next_month]) + return queryset.filter(waktu_mulai__range=[start_of_next_month, + end_of_next_month]) if self.value() == 'this_year': - return queryset.filter(waktu_donor__range=[this_year, next_year]) + return queryset.filter(waktu_mulai__year=today.year) diff --git a/backend/acara_donor/management/commands/acara_donor_seeder.py b/backend/acara_donor/management/commands/acara_donor_seeder.py index 2a25439acb0bbb80aee4d920a46786f16c3e4502..f41f3a0b9aafac7c8154559bfa042a9e51d91970 100644 --- a/backend/acara_donor/management/commands/acara_donor_seeder.py +++ b/backend/acara_donor/management/commands/acara_donor_seeder.py @@ -1,19 +1,35 @@ import random -from django.core.management.base import BaseCommand +from django.core.management.base import BaseCommand, CommandError +from main.models import User from acara_donor.factories import AcaraDonorFactory from main.factories import UserFactory class Command(BaseCommand): - help = 'Seeds the database.' + help = 'Seeds the database with dummy acara donor.' + + def add_arguments(self, parser): + parser.add_argument('--users_emails', nargs='+', type=str) def handle(self, *args, **options): list_user = [] - for _ in range(33): - user = UserFactory() - user.save() - list_user.append(user) - for _ in range(123): - i = random.randint(0, 32) # NOSONAR - AcaraDonorFactory(user=list_user[i]) - self.stdout.write(self.style.SUCCESS("Successfully seeds the database with dummy acara donor.")) + if not options['users_emails']: + for _ in range(33): + user = UserFactory() + user.save() + list_user.append(user) + else: + for email in options['users_emails']: + try: + list_user.append(User.objects.get(email=email)) + except User.DoesNotExist: + raise CommandError(f"User {email} does not exist.") + + for i in range(len(list_user)): + amount = random.randint(0, 32) # NOSONAR + for _ in range(amount): + AcaraDonorFactory(user=list_user[i]) + + self.stdout.write( + self.style.SUCCESS( + "Successfully seeds the database with dummy acara donor.")) diff --git a/backend/acara_donor/migrations/0001_initial.py b/backend/acara_donor/migrations/0001_initial.py index 8fac6bd48235f13e2c542615b8e658c2d17d69d8..547bbd980bedbdb727021648af60fa9aa2e41a52 100644 --- a/backend/acara_donor/migrations/0001_initial.py +++ b/backend/acara_donor/migrations/0001_initial.py @@ -1,10 +1,9 @@ -# Generated by Django 3.0.5 on 2020-05-04 08:29 +# Generated by Django 3.0.5 on 2020-06-02 00:45 from django.conf import settings import django.core.validators from django.db import migrations, models import django.db.models.deletion -import uuid class Migration(migrations.Migration): @@ -19,7 +18,7 @@ class Migration(migrations.Migration): migrations.CreateModel( name='AcaraDonor', fields=[ - ('nomor', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('status', models.BooleanField(null=True)), ('nama_institusi', models.CharField(max_length=95)), ('alamat_institusi', models.CharField(max_length=140)), @@ -28,14 +27,18 @@ class Migration(migrations.Migration): ('nama_koor', models.CharField(max_length=70)), ('email_koor', models.EmailField(max_length=254)), ('no_telp_koor', models.CharField(max_length=20)), + ('kategori', models.CharField(choices=[('Terbuka', 'Public'), ('Tertutup', 'Private')], max_length=20)), ('alamat_lokasi_donor', models.CharField(max_length=140)), - ('waktu_donor', models.DateTimeField()), + ('kecamatan', models.CharField(choices=[('Beji', 'Beji'), ('Pancoran Mas', 'Pancoran Mas'), ('Cipayung', 'Cipayung'), ('Sukmajaya', 'Sukmajaya'), ('Cilodong', 'Cilodong'), ('Limo', 'Limo'), ('Cinere', 'Cinere'), ('Cimanggis', 'Cimanggis'), ('Tapos', 'Tapos'), ('Sawangan', 'Sawangan'), ('Bojong Sari', 'Bojong Sari')], max_length=20)), + ('waktu_mulai', models.DateTimeField()), + ('waktu_berakhir', models.DateTimeField()), ('perkiraan_jumlah_donor', models.IntegerField(validators=[django.core.validators.MinValueValidator(11)])), ('keterangan', models.TextField(blank=True)), + ('foto_lokasi', models.ImageField(blank=True, upload_to='images')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ - 'ordering': ['waktu_donor'], + 'ordering': ['-waktu_mulai'], }, ), ] diff --git a/backend/acara_donor/migrations/0002_auto_20200505_0332.py b/backend/acara_donor/migrations/0002_auto_20200505_0332.py deleted file mode 100644 index 6cb1ad5a0dd44237fec176590b3e5fec182062ce..0000000000000000000000000000000000000000 --- a/backend/acara_donor/migrations/0002_auto_20200505_0332.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-04 20:32 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('acara_donor', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='acaradonor', - options={'ordering': ['-waktu_donor']}, - ), - ] diff --git a/backend/acara_donor/migrations/0002_auto_20200602_2055.py b/backend/acara_donor/migrations/0002_auto_20200602_2055.py new file mode 100644 index 0000000000000000000000000000000000000000..cde608cc039d8bd4dd7269ba38acebcf15e42789 --- /dev/null +++ b/backend/acara_donor/migrations/0002_auto_20200602_2055.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2020-06-02 13:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('acara_donor', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='acaradonor', + name='status', + field=models.BooleanField(default=None, null=True), + ), + ] diff --git a/backend/acara_donor/models.py b/backend/acara_donor/models.py index 727669cd16df82a44ad9e8b86f54893120c99eaa..cbe59d5a66b1a997ff631100e1b45df4c71b1710 100644 --- a/backend/acara_donor/models.py +++ b/backend/acara_donor/models.py @@ -1,28 +1,42 @@ from django.db import models from django.core.validators import MinValueValidator +from django.utils.timezone import localtime from main.models import User -import uuid +from donor.models import JadwalDonor class AcaraDonor(models.Model): - nomor = models.UUIDField(primary_key=True, default=uuid.uuid4) user = models.ForeignKey(User, on_delete=models.CASCADE) - status = models.BooleanField(null=True) + status = models.BooleanField(null=True, default=None) + nama_institusi = models.CharField(max_length=95) alamat_institusi = models.CharField(max_length=140) email_kantor = models.EmailField() no_telp_kantor = models.CharField(max_length=20) + nama_koor = models.CharField(max_length=70) email_koor = models.EmailField() no_telp_koor = models.CharField(max_length=20) + + kategori = models.CharField(max_length=20, blank=False, null=False, + choices=JadwalDonor.Category.choices) alamat_lokasi_donor = models.CharField(max_length=140) - waktu_donor = models.DateTimeField() - perkiraan_jumlah_donor = models.IntegerField(validators=[MinValueValidator(11)]) + kecamatan = models.CharField(max_length=20, blank=False, null=False, + choices=JadwalDonor.Kecamatan.choices) + waktu_mulai = models.DateTimeField() + waktu_berakhir = models.DateTimeField() + perkiraan_jumlah_donor = models.IntegerField( + validators=[MinValueValidator(11)]) keterangan = models.TextField(blank=True) + foto_lokasi = models.ImageField(upload_to='images', blank=True) def __str__(self): - waktu_donor = self.waktu_donor.strftime("%d/%m/%Y, %H:%M") - return f"{self.nama_institusi}: {self.alamat_lokasi_donor}, {waktu_donor}" + waktu_mulai = localtime(self.waktu_mulai) + waktu_berakhir = localtime(self.waktu_berakhir) + return f"{self.nama_institusi}: {self.alamat_lokasi_donor}, \ + {waktu_mulai.date()} \ + ({waktu_mulai.hour:02d}:{waktu_mulai.minute:02d} \ + - {waktu_berakhir.hour:02d}:{waktu_berakhir.minute:02d})" class Meta: - ordering = ['-waktu_donor'] + ordering = ['-waktu_mulai'] diff --git a/backend/acara_donor/serializers.py b/backend/acara_donor/serializers.py index 6b26383635d60dc7c1e94dfca30eb785d07fa2be..ccce4f8110f715cba8bbfc6bd419c8b21b057fe5 100644 --- a/backend/acara_donor/serializers.py +++ b/backend/acara_donor/serializers.py @@ -5,15 +5,26 @@ from acara_donor.models import AcaraDonor class AcaraDonorSerializer(serializers.ModelSerializer): def validate(self, data): - queryset = AcaraDonor.objects.filter(nama_institusi=data['nama_institusi'], - alamat_lokasi_donor=data['alamat_lokasi_donor'], - waktu_donor=data['waktu_donor']) + queryset = AcaraDonor.objects.filter( + nama_institusi=data['nama_institusi'], + alamat_lokasi_donor=data['alamat_lokasi_donor'], + waktu_mulai=data['waktu_mulai']) if queryset.exists(): - raise serializers.ValidationError("Acara donor darah ini sudah diajukan.") - if data['waktu_donor'] < timezone.now(): - raise serializers.ValidationError("Tanggal acara donor darah tidak boleh di masa lalu.") + raise serializers.ValidationError( + "Acara donor darah ini sudah diajukan.") + + if data['waktu_mulai'] < timezone.now(): + raise serializers.ValidationError( + "Pelaksanaan acara donor darah tidak boleh di masa lalu.") + + if data['waktu_berakhir'] < data['waktu_mulai']: + raise serializers.ValidationError( + "Jam pelaksanaan acara donor darah tidak valid.") + return data class Meta: model = AcaraDonor - exclude = ["user", "status"] + exclude = ["user", ] + extra_kwargs = {'foto_lokasi': {'required': False}, + 'status': {'default': None}} diff --git a/backend/acara_donor/test_commands.py b/backend/acara_donor/test_commands.py index c25cfa4a92b4c4aaad473660026e9c133451643b..5eb15c9f8866c35536968673d0193ddfcfa3faaf 100644 --- a/backend/acara_donor/test_commands.py +++ b/backend/acara_donor/test_commands.py @@ -1,7 +1,8 @@ from io import StringIO -from django.core.management import call_command +from django.core.management import call_command, CommandError from django.test import TestCase +from main.factories import UserFactory class SeedAcaraDonorTest(TestCase): @@ -9,3 +10,22 @@ class SeedAcaraDonorTest(TestCase): out = StringIO() call_command('acara_donor_seeder', stdout=out) self.assertIn('Success', out.getvalue()) + + def test_command_with_email_provided_success(self): + user1 = UserFactory(email="tes1@gmail.com") + user2 = UserFactory(email="tes2@gmail.com") + user1.save() + user2.save() + out = StringIO() + call_command( + 'acara_donor_seeder', users_emails=["tes1@gmail.com", + "tes2@gmail.com"], + stdout=out) + self.assertIn('Success', out.getvalue()) + + def test_command_with_email_provided_failed(self): + out = StringIO() + with self.assertRaisesMessage(CommandError, + 'User bities@gmail.com does not exist.'): + call_command('acara_donor_seeder', + users_emails=["bities@gmail.com", ], stdout=out) diff --git a/backend/acara_donor/tests.py b/backend/acara_donor/tests.py index aa3d754c8f80f8aec70d939992ad940e8a446681..b8104eebe284935704c0671b19333f851e75ddad 100644 --- a/backend/acara_donor/tests.py +++ b/backend/acara_donor/tests.py @@ -1,5 +1,8 @@ +import datetime +from unittest.mock import patch from django.test import TestCase from django.utils import timezone +from django.utils.timezone import localtime from rest_framework import status from rest_framework.test import APITestCase from rest_framework_authlib.tokens import AccessToken @@ -17,13 +20,18 @@ class AcaraDonorTest(TestCase): user = UserFactory() user.save() acara_donor = AcaraDonorFactory(user=user) - waktu_donor = acara_donor.waktu_donor.strftime("%d/%m/%Y, %H:%M") + waktu_mulai = localtime(acara_donor.waktu_mulai) + waktu_berakhir = localtime(acara_donor.waktu_berakhir) self.assertTrue(isinstance(acara_donor, AcaraDonor)) - self.assertEqual((str(acara_donor.nama_institusi) + ': ' + - str(acara_donor.alamat_lokasi_donor) + ", " + waktu_donor), + self.assertEqual((f"{acara_donor.nama_institusi}: {acara_donor.alamat_lokasi_donor}, \ + {waktu_mulai.date()} \ + ({waktu_mulai.hour:02d}:{waktu_mulai.minute:02d} \ + - {waktu_berakhir.hour:02d}:{waktu_berakhir.minute:02d})"), str(acara_donor)) +@patch('django.utils.timezone.now', return_value=datetime.datetime( + 2020, 6, 12, 9, 0, tzinfo=datetime.timezone.utc)) class AcaraDonorSerializerTest(TestCase): """ Test module for Acara Donor serializer""" @@ -31,60 +39,145 @@ class AcaraDonorSerializerTest(TestCase): self.user = UserFactory() self.user.save() self.acara_donor = AcaraDonorFactory(user=self.user) + base_time = timezone.localtime( + datetime.datetime(2020, 6, 12, 9, 18, + tzinfo=datetime.timezone.utc)) self.acara_donor_attr = {"alamat_institusi": "Pacilkom", "alamat_lokasi_donor": "Sekre Pacil", "email_kantor": "pacil@cs.ui.ac.id", "email_koor": "pacilia@gmail.com", + "kategori": "Terbuka", + "kecamatan": "Beji", "keterangan": "", "nama_institusi": "Pacilkom", "nama_koor": "dr. Pacilia", "no_telp_kantor": "08165342342", "no_telp_koor": "08167021743", - "nomor": "ea582779-fdfc-444e-ad03-4084e715af0a", "perkiraan_jumlah_donor": 455, - "waktu_donor": timezone.now() + timezone.timedelta(days=3)} + "waktu_berakhir": base_time + + timezone.timedelta(hours=5), + "waktu_mulai": base_time + + timezone.timedelta(hours=3)} + + def test_create_acara_donor_failed_due_to_duplication(self, + mock_timezone): + serializer = AcaraDonorSerializer(instance=self.acara_donor, + data=self.acara_donor_attr) - def test_create_acara_donor_failed_due_to_duplication(self): - serializer = AcaraDonorSerializer(instance=self.acara_donor, data=self.acara_donor_attr) serializer.is_valid() new_acara_donor = serializer.save() new_acara_donor.refresh_from_db() - serializer = AcaraDonorSerializer(instance=self.acara_donor, data=self.acara_donor_attr) + serializer = AcaraDonorSerializer(instance=self.acara_donor, + data=self.acara_donor_attr) + self.assertFalse(serializer.is_valid()) + + def test_create_acara_donor_failed_because_time_has_passed(self, + mock_timezone): + self.acara_donor_attr['waktu_mulai'] = timezone.now() \ + - timezone.timedelta(days=3) + serializer = AcaraDonorSerializer(instance=self.acara_donor, + data=self.acara_donor_attr) self.assertFalse(serializer.is_valid()) - def test_create_acara_donor_failed_because_time_has_passed(self): - self.acara_donor_attr['waktu_donor'] = timezone.now() - timezone.timedelta(days=3) - serializer = AcaraDonorSerializer(instance=self.acara_donor, data=self.acara_donor_attr) + def test_create_acara_donor_failed_because_end_time_is_before_start_time( + self, mock_timezone): + self.acara_donor_attr['waktu_mulai'] = timezone.now() \ + + timezone.timedelta(hours=5) + self.acara_donor_attr['waktu_berakhir'] = timezone.now() \ + + timezone.timedelta(hours=3) + serializer = AcaraDonorSerializer(instance=self.acara_donor, + data=self.acara_donor_attr) self.assertFalse(serializer.is_valid()) +@patch('django.utils.timezone.now', return_value=datetime.datetime( + 2020, 6, 12, 9, 0, tzinfo=datetime.timezone.utc)) class AcaraDonorCreateViewTest(APITestCase): """ Test module for Acara Donor API""" def setUp(self): self.user = UserFactory() self.user.save() - self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + str(AccessToken.for_user(self.user))) - - def test_create_acara_donor(self): + base_time = timezone.localtime(datetime.datetime( + 2020, 6, 12, 9, 18, tzinfo=datetime.timezone.utc)) + self.data = {"alamat_institusi": "Pacilkom", + "alamat_lokasi_donor": "Sekre Pacil", + "email_kantor": "pacil@cs.ui.ac.id", + "email_koor": "pacilia@gmail.com", + "foto_lokasi": "", + "kategori": "Terbuka", + "kecamatan": "Beji", + "keterangan": "", + "nama_institusi": "Pacilkom", + "nama_koor": "dr. Pacilia", + "no_telp_kantor": "08165342342", + "no_telp_koor": "08167021743", + "perkiraan_jumlah_donor": 455, + "waktu_berakhir": base_time + + timezone.timedelta(hours=7), + "waktu_mulai": base_time + + timezone.timedelta(hours=5)} + + def test_create_acara_donor_with_post_request(self, mock_timezone): + self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + + str(AccessToken.for_user(self.user))) response = self.client.post('/acara-donor/pengajuan/', - {"alamat_institusi": "Pacilkom", - "alamat_lokasi_donor": "Sekre Pacil", - "email_kantor": "pacil@cs.ui.ac.id", - "email_koor": "pacilia@gmail.com", - "keterangan": "", - "nama_institusi": "Pacilkom", - "nama_koor": "dr. Pacilia", - "no_telp_kantor": "08165342342", - "no_telp_koor": "08167021743", - "nomor": "ea582779-fdfc-444e-ad03-4084e715af0a", - "perkiraan_jumlah_donor": 455, - "waktu_donor": timezone.now() + timezone.timedelta(days=3)}, - format='json') + self.data, + format='multipart') self.assertEqual(response.status_code, status.HTTP_201_CREATED) + def test_create_acara_donor_unauthorized(self, mock_timezone): + response = self.client.post('/acara-donor/pengajuan/', + self.data, + format='multipart') + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + +class RiwayatAcaraDonorCreateViewTest(APITestCase): + """ Test module for Riwayat Acara Donor API""" + def setUp(self): + self.user = UserFactory() + self.user.save() + self.acara_donor = AcaraDonorFactory( + user=self.user, + waktu_mulai=timezone.now() + timezone.timedelta(days=1)) + self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + + str(AccessToken.for_user(self.user))) + + def test_get_riwayat_acara_donor_unauthorized(self): + self.client.credentials() + response = self.client.get('/acara-donor/pengajuan/riwayat/') + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_pagination_is_three(self): + for _ in range(5): + AcaraDonorFactory(user=self.user) + response = self.client.get('/acara-donor/pengajuan/riwayat/') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['count'] == 6) + self.assertTrue(len(response.data['results']) == 3) + + def test_list_all_acara_donor(self): + for _ in range(4): + AcaraDonorFactory(user=self.user) + # Get second page and confirm it has exactly remaining 2 items + response = self.client.get('/acara-donor/pengajuan/riwayat/?page=2') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['count'] == 5) + self.assertTrue(len(response.data['results']) == 2) + + def test_page_not_found(self): + for _ in range(3): + AcaraDonorFactory(user=self.user) + # Get third page when it only has 4 items + response = self.client.get('/acara-donor/pengajuan/riwayat/?page=3') + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + +@patch('acara_donor.filters.now', new=lambda: + datetime.datetime(2020, 5, 10, 9, 0, tzinfo=datetime.timezone.utc)) class AcaraDonorFilterTest(TestCase): """ Test module for Acara Donor filter""" @@ -92,39 +185,61 @@ class AcaraDonorFilterTest(TestCase): self.user = UserFactory() self.user.save() - self.acara_donor_today = AcaraDonorFactory(user=self.user, - waktu_donor=timezone.now() + timezone.timedelta(hours=3)) + base_time = timezone.localtime(datetime.datetime( + 2020, 5, 10, 9, 14, tzinfo=datetime.timezone.utc)) + + self.acara_donor_today = AcaraDonorFactory( + user=self.user, + waktu_mulai=base_time + timezone.timedelta(hours=3)) self.acara_donor_today.save() - self.acara_donor_overmorrow = AcaraDonorFactory(user=self.user, - waktu_donor=timezone.now() + timezone.timedelta(days=2)) + self.acara_donor_overmorrow = AcaraDonorFactory( + user=self.user, + waktu_mulai=base_time + timezone.timedelta(days=2)) self.acara_donor_overmorrow.save() - self.acara_donor_next_month = AcaraDonorFactory(user=self.user, - waktu_donor=timezone.now() + timezone.timedelta(days=33)) + self.acara_donor_next_month = AcaraDonorFactory( + user=self.user, + waktu_mulai=base_time + timezone.timedelta(days=33)) self.acara_donor_next_month.save() def test_filter_acara_donor_today_success(self): - filter_today = AcaraDonorFilter(None, {'waktu_donor': 'today'}, AcaraDonor, AcaraDonorAdmin) - acara_donor_filtered = filter_today.queryset(None, AcaraDonor.objects.all())[0] - self.assertEqual(acara_donor_filtered.waktu_donor, self.acara_donor_today.waktu_donor) + filter_today = AcaraDonorFilter(None, {'waktu_mulai': 'today'}, + AcaraDonor, AcaraDonorAdmin) + acara_donor_filtered = filter_today.queryset( + None, AcaraDonor.objects.all())[0] + self.assertEqual(acara_donor_filtered.waktu_mulai, + self.acara_donor_today.waktu_mulai) def test_filter_acara_donor_next_seven_days_success(self): - filter_seven_days = AcaraDonorFilter(None, {'waktu_donor': 'next_seven_days'}, AcaraDonor, AcaraDonorAdmin) - acara_donor_filtered = filter_seven_days.queryset(None, AcaraDonor.objects.all())[0] - self.assertEqual(acara_donor_filtered.waktu_donor, self.acara_donor_overmorrow.waktu_donor) + filter_seven_days = AcaraDonorFilter( + None, {'waktu_mulai': 'next_seven_days'}, + AcaraDonor, AcaraDonorAdmin) + acara_donor_filtered = filter_seven_days.queryset( + None, AcaraDonor.objects.all())[0] + self.assertEqual(acara_donor_filtered.waktu_mulai, + self.acara_donor_overmorrow.waktu_mulai) def test_filter_acara_donor_this_month_success(self): - filter_this_month = AcaraDonorFilter(None, {'waktu_donor': 'this_month'}, AcaraDonor, AcaraDonorAdmin) - acara_donor_filtered = filter_this_month.queryset(None, AcaraDonor.objects.all())[0] - self.assertEqual(acara_donor_filtered.waktu_donor, self.acara_donor_overmorrow.waktu_donor) + filter_this_month = AcaraDonorFilter( + None, {'waktu_mulai': 'this_month'}, AcaraDonor, AcaraDonorAdmin) + acara_donor_filtered = filter_this_month.queryset( + None, AcaraDonor.objects.all())[0] + self.assertEqual(acara_donor_filtered.waktu_mulai, + self.acara_donor_overmorrow.waktu_mulai) def test_filter_acara_donor_next_month_success(self): - filter_next_month = AcaraDonorFilter(None, {'waktu_donor': 'next_month'}, AcaraDonor, AcaraDonorAdmin) - acara_donor_filtered = filter_next_month.queryset(None, AcaraDonor.objects.all())[0] - self.assertEqual(acara_donor_filtered.waktu_donor, self.acara_donor_next_month.waktu_donor) + filter_next_month = AcaraDonorFilter( + None, {'waktu_mulai': 'next_month'}, AcaraDonor, AcaraDonorAdmin) + acara_donor_filtered = filter_next_month.queryset( + None, AcaraDonor.objects.all())[0] + self.assertEqual(acara_donor_filtered.waktu_mulai, + self.acara_donor_next_month.waktu_mulai) def test_filter_acara_donor_this_year_success(self): - filter_this_year = AcaraDonorFilter(None, {'waktu_donor': 'this_year'}, AcaraDonor, AcaraDonorAdmin) - acara_donor_filtered = filter_this_year.queryset(None, AcaraDonor.objects.all())[0] - self.assertEqual(acara_donor_filtered.waktu_donor, self.acara_donor_next_month.waktu_donor) + filter_this_year = AcaraDonorFilter(None, {'waktu_mulai': 'this_year'}, + AcaraDonor, AcaraDonorAdmin) + acara_donor_filtered = filter_this_year.queryset( + None, AcaraDonor.objects.all())[0] + self.assertEqual(acara_donor_filtered.waktu_mulai, + self.acara_donor_next_month.waktu_mulai) diff --git a/backend/acara_donor/urls.py b/backend/acara_donor/urls.py index a5c6198471f45d3d1f50599f402e416826128a96..2f70c7f32aafa4054f966bede857fc311d0d9572 100644 --- a/backend/acara_donor/urls.py +++ b/backend/acara_donor/urls.py @@ -1,7 +1,9 @@ from django.urls import path -from acara_donor.views import AcaraDonorView +from acara_donor.views import AcaraDonorView, RiwayatAcaraDonorView urlpatterns = [ - path('pengajuan/', AcaraDonorView.as_view(), name="acara-donor") + path('pengajuan/', AcaraDonorView.as_view(), name="acara-donor"), + path('pengajuan/riwayat/', RiwayatAcaraDonorView.as_view(), + name="riwayat-acara-donor") ] diff --git a/backend/acara_donor/views.py b/backend/acara_donor/views.py index 327d8a6ee3e80211dccf29e1dbc2453422b59d37..18c6759cd23d053f7e8dfc988814370320661b2c 100644 --- a/backend/acara_donor/views.py +++ b/backend/acara_donor/views.py @@ -1,6 +1,8 @@ from rest_framework import generics from rest_framework.permissions import IsAuthenticated +from acara_donor.models import AcaraDonor from acara_donor.serializers import AcaraDonorSerializer +from main.paginations import ExtraSmallResultsSetPagination class AcaraDonorView(generics.CreateAPIView): @@ -9,3 +11,13 @@ class AcaraDonorView(generics.CreateAPIView): def perform_create(self, serializer): serializer.save(user=self.request.user) + + +class RiwayatAcaraDonorView(generics.ListAPIView): + serializer_class = AcaraDonorSerializer + permission_classes = [IsAuthenticated] + pagination_class = ExtraSmallResultsSetPagination + + def get_queryset(self): + return AcaraDonor.objects.filter( + user=self.request.user).order_by('-waktu_mulai') diff --git a/backend/dblood/settings/common.py b/backend/dblood/settings/common.py index 280ae74ff772f354bead7f457d63f1f7d9b0d9a9..776270a4954612b7ef898694265c44e0a11cf48e 100644 --- a/backend/dblood/settings/common.py +++ b/backend/dblood/settings/common.py @@ -15,7 +15,7 @@ import os import dj_database_url # Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # Application definition @@ -28,11 +28,13 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'rest_framework', 'rest_framework_authlib', + 'kronos', 'main', 'stok_darah', 'anymail', 'corsheaders', 'donor', + 'edukasi', 'acara_donor' ] @@ -47,6 +49,10 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] +CRON_CLASSES = [ + 'donor.crons.ClearFormulirDaftarDonor' +] + ROOT_URLCONF = 'dblood.urls' TEMPLATES = [ diff --git a/backend/dblood/settings/production.py b/backend/dblood/settings/production.py index e329ebd9e0feb4e1e5529cf0c72e432a0a524044..43831b54fa2c3b99f694bdbe1dba025bfa0d24d8 100644 --- a/backend/dblood/settings/production.py +++ b/backend/dblood/settings/production.py @@ -24,3 +24,7 @@ USE_X_FORWARDED_HOST = os.getenv('USE_X_FORWARDED_HOST', 'False') == 'True' USE_X_FORWARDED_PORT = os.getenv('USE_X_FORWARDED_PORT', 'False') == 'True' SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') + +MEDIA_URL = os.getenv('MEDIA_URL', '/media/') + +MEDIA_ROOT = os.getenv('MEDIA_ROOT', os.path.join(BASE_DIR, 'media')) diff --git a/backend/dblood/settings/staging.py b/backend/dblood/settings/staging.py index 4c70b24d771ff121e80f43fc088640fb6a699b8f..e889c78e834aa38e584839b23f5909fce0b4e6c0 100644 --- a/backend/dblood/settings/staging.py +++ b/backend/dblood/settings/staging.py @@ -1,3 +1,5 @@ +import os + from .production import * SECRET_KEY = os.getenv('SECRET_KEY', '__super_secret_high_entropy_pseudo_random_bytes_a608e1fb__') diff --git a/backend/dblood/urls.py b/backend/dblood/urls.py index fc9291f0e4f77c281db4989d2268c220fb5d2784..508561e535dd5c9b1118e65c9dab9565d593d429 100644 --- a/backend/dblood/urls.py +++ b/backend/dblood/urls.py @@ -13,13 +13,16 @@ Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ +from django.conf import settings +from django.conf.urls.static import static from django.contrib import admin -from django.urls import path, include +from django.urls import include, path urlpatterns = [ path('admin/', admin.site.urls), path('', include('main.urls')), path('donor/', include('donor.urls')), + path('edukasi/', include('edukasi.urls')), path('acara-donor/', include('acara_donor.urls')), path('stok-darah/', include('stok_darah.urls')) -] +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/backend/dev.Dockerfile b/backend/dev.Dockerfile index f7277f7375bfb8564fbfa74e908fd249de5b046a..1cd4011039f729f64dda834d0063388dfe031749 100644 --- a/backend/dev.Dockerfile +++ b/backend/dev.Dockerfile @@ -1,7 +1,7 @@ FROM python:3.8-buster RUN apt-get update -q && \ - apt-get install -y libpq-dev python3-dev + apt-get install -y libpq-dev python3-dev cron WORKDIR /app COPY requirements.txt /app/ @@ -16,5 +16,6 @@ ENV DATABASE_URL 'sqlite:///db.sqlite3' ENV DJANGO_SETTINGS_MODULE 'dblood.settings.staging' ENV STATIC_URL '/staging/api/static/' -RUN ["python3", "manage.py", "collectstatic"] +RUN ["python", "manage.py", "installtasks"] + CMD ["gunicorn", "dblood.wsgi", "--bind", "0.0.0.0:8000"] diff --git a/backend/donor/admin.py b/backend/donor/admin.py index e6ca278bead9877b93633e1b891df63398948304..74e0a582505782ce2e5922913460e92019db1683 100644 --- a/backend/donor/admin.py +++ b/backend/donor/admin.py @@ -1,13 +1,108 @@ +from django.conf import settings from django.contrib import admin +from django.db.models import Q +from django.utils.translation import gettext_lazy as _ -from donor.models import JadwalDonor, DaftarDonor +from .models import JadwalDonor, DaftarDonor +from .services import jadwal_donor_spreadsheet_service +from main.admin import InputFilter + + +def download_jadwaldonor(modeladmin, request, queryset): + return jadwal_donor_spreadsheet_service.download_jadwaldonor(modeladmin, queryset) + + +download_jadwaldonor.short_description = 'Download Jadwal Donor as Excel Workbook' @admin.register(JadwalDonor) class JadwalDonorAdmin(admin.ModelAdmin): list_filter = ('kecamatan', 'time_start', 'category') + search_fields = ('time_start', 'location') + actions = [download_jadwaldonor] + + class Media: + js = [settings.MEDIA_URL+'/js/acara_donor_custom_admin.js', ] + + +class JadwalDonorFilter(InputFilter): + parameter_name = 'lokasi_donor' + title = _('lokasi donor') + + def queryset(self, request, queryset): + if self.value() is not None: + lokasi_donor = self.value() + return queryset.filter( + Q(jadwal_donor__location__contains=lokasi_donor) + ) @admin.register(DaftarDonor) class DaftarDonorAdmin(admin.ModelAdmin): - list_filter = ('has_attended', ) + list_display = ('user', 'has_attended', 'get_goldar', 'jadwal_donor') + search_fields = ('user__email', + 'jadwal_donor__location', 'jadwal_donor__time_start') + fields = ('has_attended', + 'id', + 'user', + 'jadwal_donor', + 'merasa_sehat', + 'minum_antibiotik', + 'minum_obat_infeksi', + 'minum_aspirin', + 'sakit_kepala_dan_demam', + 'sedang_hamil', + 'kehamilan_berapa', + 'donor_darah_trombosit_plasma', + 'menerima_vaksinasi', + 'kontak_vaksinasi', + 'donor_aferesis', + 'pernah_transfusi', + 'pernah_transplasi', + 'pernah_cangkok_tulang', + 'pernah_tusuk_jarum_medis', + 'pernah_seks_aids', + 'pernah_seks_psk', + 'pernah_seks_narkoba', + 'pernah_seks_konsentrat', + 'wanita_pernah_seks_laki_biseksual', + 'pernah_seks_dengan_hepatitis', + 'tinggal_dengan_hepatitis', + 'punya_tato', + 'punya_tindik', + 'sedang_sifilis_go', + 'pernah_dipenjara', + 'pernah_di_luar_indonesia', + 'pernah_jualan_seks', + 'pernah_homoseks', + 'pernah_tinggal_di_eropa', + 'pernah_terima_transfusi_di_inggris', + 'pernah_tinggal_di_inggris', + 'positif_aids', + 'pakai_jarum_suntik', + 'pakai_konsentrat', + 'menderita_hepatitis', + 'menderita_malaria', + 'menderita_kanker', + 'bermasalah_jantung_paru_paru', + 'menderita_pendarahan', + 'seks_dengan_orang_afrika', + 'tinggal_di_afrika') + list_filter = ('has_attended', JadwalDonorFilter, 'jadwal_donor__time_start', 'user__profile__blood_type') + + def get_goldar(self, obj): + return obj.user.profile.blood_type + get_goldar.short_description = 'Goldar' + get_goldar.admin_order_field = 'user__profile__blood_type' + + def get_readonly_fields(self, request, obj=None): + readonly_fields = list(set( + [field.name for field in self.opts.local_fields] + + [field.name for field in self.opts.local_many_to_many] + )) + if 'has_attended' in readonly_fields: + readonly_fields.remove('has_attended') + return readonly_fields + + class Media: + js = [settings.MEDIA_URL+'/js/daftar_donor_custom_admin.js', ] diff --git a/backend/donor/management/commands/clear_formulir_daftar_donor.py b/backend/donor/management/commands/clear_formulir_daftar_donor.py new file mode 100644 index 0000000000000000000000000000000000000000..d02cc06532450be384bfdd11c0ad7b9f01aac216 --- /dev/null +++ b/backend/donor/management/commands/clear_formulir_daftar_donor.py @@ -0,0 +1,28 @@ +import glob +import os +from datetime import datetime + +import kronos +from django.conf import settings +from django.core.management import BaseCommand + + +@kronos.register('0 0 * * *') +class Command(BaseCommand): + help = 'Clear unused formulir daftar donor' + + def handle(self, *args, **options): + formulir_folder = 'formulir-daftar-donor' + all_files = glob.glob(os.path.join(settings.MEDIA_ROOT, formulir_folder, "*.pdf")) + now = datetime.now() + cnt_removed = 0 + for file in all_files: + statbuf = os.stat(file) + modified_time = datetime.fromtimestamp(statbuf.st_mtime) + elapsed_time = now - modified_time + if elapsed_time.days > 1: + os.remove(file) + cnt_removed += 1 + + self.stdout.write(self.style.SUCCESS( + "Successfully clear {:d} unused formulir daftar donor".format(cnt_removed))) diff --git a/backend/donor/management/commands/seed_agenda_donor.py b/backend/donor/management/commands/seed_agenda_donor.py index 0e751c5edf210a9a84bff91544602120dbbb540e..2a6b5667bac6f1e0e68a00c0832c347f2f297673 100644 --- a/backend/donor/management/commands/seed_agenda_donor.py +++ b/backend/donor/management/commands/seed_agenda_donor.py @@ -27,15 +27,14 @@ class Command(BaseCommand): ) if options['users_email'] else list(User.objects.all()) list_daftar_donor = [] - ada = {} for jadwal_donor in list_jadwal_donor: random.shuffle(list_user) - for i in range(rand_int(0, min(5, len(list_user)))): + jumlah_pendaftar = rand_int(0, len(list_user)) + for i in range(jumlah_pendaftar): user = list_user[i] if DaftarDonor.objects.filter(user=user, jadwal_donor=jadwal_donor).count() > 0: continue sedang_hamil = rand_bool() - ada[(user.id, jadwal_donor.id)] = True list_daftar_donor.append( DaftarDonor( user=user, diff --git a/backend/donor/migrations/0011_auto_20200602_0227.py b/backend/donor/migrations/0011_auto_20200602_0227.py new file mode 100644 index 0000000000000000000000000000000000000000..305090c089e8d0f0d5587d810287d4b6f3fcdfa0 --- /dev/null +++ b/backend/donor/migrations/0011_auto_20200602_0227.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2020-06-01 19:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('donor', '0010_auto_20200427_0713'), + ] + + operations = [ + migrations.AlterField( + model_name='jadwaldonor', + name='category', + field=models.CharField(choices=[('Terbuka', 'Public'), ('Tertutup', 'Private')], max_length=20), + ), + ] diff --git a/backend/donor/migrations/0012_auto_20200604_1236.py b/backend/donor/migrations/0012_auto_20200604_1236.py new file mode 100644 index 0000000000000000000000000000000000000000..9cfecdf94717178a15af237159fffa460484f8e6 --- /dev/null +++ b/backend/donor/migrations/0012_auto_20200604_1236.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2020-06-04 05:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('donor', '0011_auto_20200602_0227'), + ] + + operations = [ + migrations.AlterField( + model_name='jadwaldonor', + name='location', + field=models.CharField(max_length=140), + ), + ] diff --git a/backend/donor/models.py b/backend/donor/models.py index 31209d9669ec4d1a40cbe5bd163d35a022d4b60e..6f4c4d3bbc01102e368a3d38b50bf1c8a29bdf70 100644 --- a/backend/donor/models.py +++ b/backend/donor/models.py @@ -1,5 +1,5 @@ from django.core.exceptions import ValidationError -from django.core.validators import MinValueValidator, MaxValueValidator +from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.utils.timezone import localtime @@ -22,11 +22,11 @@ class JadwalDonor(models.Model): BOJONG_SARI = "Bojong Sari" class Category(models.TextChoices): - PUBLIC = "terbuka" - PRIVATE = "tertutup" + PUBLIC = "Terbuka" + PRIVATE = "Tertutup" kecamatan = models.CharField(max_length=20, choices=Kecamatan.choices) - location = models.CharField(max_length=30) + location = models.CharField(max_length=140) time_start = models.DateTimeField() time_end = models.DateTimeField() quota = models.PositiveIntegerField() @@ -97,10 +97,14 @@ class DaftarDonor(models.Model): seks_dengan_orang_afrika = models.BooleanField() tinggal_di_afrika = models.BooleanField() + def __str__(self): + return str(self.user) + " - " + str(self.jadwal_donor) + + @classmethod + def download_query(cls, jadwal_qs): + return cls.objects.filter(jadwal_donor__in=jadwal_qs, has_attended=True) + class Meta: constraints = [ models.UniqueConstraint(fields=['user', 'jadwal_donor'], name='unique__user-jadwal_donor') ] - - def __str__(self): - return str(self.user) + " - " + str(self.jadwal_donor) diff --git a/backend/donor/resources/DejaVuSans.ttf b/backend/donor/resources/DejaVuSans.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e5f7eecce43be41ff0703ed99e1553029b849f14 Binary files /dev/null and b/backend/donor/resources/DejaVuSans.ttf differ diff --git a/backend/donor/resources/form-a4-data_diri.jpg b/backend/donor/resources/form-a4-data_diri.jpg new file mode 100644 index 0000000000000000000000000000000000000000..acf0dbe3fc0b6597ce427b33255a11bb5cdb5ce7 Binary files /dev/null and b/backend/donor/resources/form-a4-data_diri.jpg differ diff --git a/backend/donor/resources/form-a4-kondisi.jpg b/backend/donor/resources/form-a4-kondisi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..60e979bcdea2fd0fb5199dc1e57bbac67db772eb Binary files /dev/null and b/backend/donor/resources/form-a4-kondisi.jpg differ diff --git a/backend/donor/services.py b/backend/donor/services.py new file mode 100644 index 0000000000000000000000000000000000000000..ae12772bfa7d02ac81be5dad91afcb0e0820c721 --- /dev/null +++ b/backend/donor/services.py @@ -0,0 +1,363 @@ +from datetime import datetime +import os + +from dateutil.relativedelta import relativedelta +from django.conf import settings +from django.http import HttpResponse +from django_pandas.io import read_frame +import pandas as pd +from PIL import Image, ImageDraw, ImageFont + +from main.utils.numbers import last_digit, second_last_digit +from main.utils.python import default_kwargs_with_override + +from .models import DaftarDonor + +resource_path = os.path.join(settings.BASE_DIR, "donor", "resources") + + +class FormulirDaftarDonorService: + + def _draw_formulir_data_diri(self, daftar_donor, image): + font_path = os.path.join(resource_path, "DejaVuSans.ttf") + regular_font = ImageFont.truetype(font_path, 24) + unicode_font = ImageFont.truetype(font_path, 40) + + user = daftar_donor.user + profile = user.profile + draw = ImageDraw.Draw(image) + kwargs = { + "fill": (0, 0, 0), + "font": regular_font + } + + first_column_x = 135 + second_column_x = 720 + first_row_y = 265 + delta_y = 42 + draw.text(xy=(first_column_x, first_row_y), text=profile.id_card_no, **kwargs) + draw.text(xy=(first_column_x, first_row_y + delta_y), text=user.first_name, **kwargs) + draw.text(xy=(first_column_x, first_row_y + 2*delta_y), text=profile.address, **kwargs) + draw.text(xy=(first_column_x, first_row_y + 4*delta_y), text=profile.phone_no, **kwargs) + draw.text(xy=(first_column_x, first_row_y + 6*delta_y), text=profile.work_address, **kwargs) + draw.text(xy=(first_column_x, first_row_y + 7*delta_y), + text=profile.work_phone_no if len(profile.work_phone_no) > 6 else "", **kwargs) + draw.text(xy=(first_column_x, first_row_y + int(9.8*delta_y)), text=profile.birthplace, **kwargs) + draw.text(xy=(first_column_x + 10, first_row_y + 12*delta_y), + text=str(second_last_digit(profile.birthdate.day)), **kwargs) + draw.text(xy=(first_column_x + 10 + 50, first_row_y + 12*delta_y), + text=str(last_digit(profile.birthdate.day)), **kwargs) + draw.text(xy=(first_column_x + 160, first_row_y + 12*delta_y), + text=str(second_last_digit(profile.birthdate.month)), **kwargs) + draw.text(xy=(first_column_x + 160 + 50, first_row_y + 12*delta_y), + text=str(last_digit(profile.birthdate.month)), **kwargs) + draw.text(xy=(first_column_x + 315, first_row_y + 12*delta_y), + text=str(second_last_digit(profile.birthdate.year)), **kwargs) + draw.text(xy=(first_column_x + 315 + 50, first_row_y + 12*delta_y), + text=str(last_digit(profile.birthdate.year)), **kwargs) + + draw.text(xy=(second_column_x + (125 if profile.sex == 'F' else 0), first_row_y + int(1.1*delta_y)), + text="✔", **default_kwargs_with_override(kwargs, {"font": unicode_font})) + draw.text(xy=(second_column_x, first_row_y + 2*delta_y), text=profile.village, **kwargs) + draw.text(xy=(second_column_x, first_row_y + 3*delta_y), text=profile.district, **kwargs) + draw.text(xy=(second_column_x, first_row_y + 4*delta_y), text=profile.city, **kwargs) + draw.text(xy=(second_column_x, first_row_y + 5*delta_y), text=profile.married_status, **kwargs) + + def _draw_formulir_kondisi(self, daftar_donor, image): + font_path = os.path.join(resource_path, "DejaVuSans.ttf") + regular_font = ImageFont.truetype(font_path, 24) + unicode_font = ImageFont.truetype(font_path, 40) + + draw = ImageDraw.Draw(image) + kwargs = { + "text": "✔", + "fill": (0, 0, 0), + "font": unicode_font + } + first_column_yes_x = 1055 + first_column_no_x = 1130 + second_column_yes_x = 2230 + second_column_no_x = 2305 + delta_y = 33 + + first_column_fields_with_start_y = [ + { + "start_y": 300, + "fields": [ + "merasa_sehat", + "minum_antibiotik", + "minum_obat_infeksi", + ] + }, + { + "start_y": 435, + "fields": [ + "minum_aspirin", + ] + }, + { + "start_y": 500, + "fields": [ + "sakit_kepala_dan_demam", + ] + }, + { + "start_y": 595, + "fields": [ + "sedang_hamil", + ] + }, + { + "start_y": 656, + "fields": [ + "donor_darah_trombosit_plasma", + "menerima_vaksinasi", + "kontak_vaksinasi", + ] + }, + { + "start_y": 785, + "fields": [ + "donor_aferesis", + ] + }, + { + "start_y": 850, + "fields": [ + "pernah_transfusi", + "pernah_transplasi", + "pernah_cangkok_tulang", + "pernah_tusuk_jarum_medis", + "pernah_seks_aids", + "pernah_seks_psk", + "pernah_seks_narkoba", + "pernah_seks_konsentrat", + "wanita_pernah_seks_laki_biseksual", + "pernah_seks_dengan_hepatitis", + "tinggal_dengan_hepatitis", + "punya_tato", + "punya_tindik", + "sedang_sifilis_go", + "pernah_dipenjara", + ] + } + ] + for column_with_start_y in first_column_fields_with_start_y: + start_y = column_with_start_y["start_y"] + fields = column_with_start_y["fields"] + for i, field in enumerate(fields): + x = first_column_yes_x if getattr(daftar_donor, field) is True else first_column_no_x + draw.text(xy=(x, start_y + i*delta_y), **kwargs) + + if daftar_donor.sedang_hamil: + draw.text(xy=(950, 595), **default_kwargs_with_override(kwargs, { + "text": str(daftar_donor.kehamilan_berapa), + "font": regular_font + })) + + second_column_fields_with_start_y = [ + { + "start_y": 333, + "fields": [ + "pernah_di_luar_indonesia", + ] + }, + { + "start_y": 430, + "fields": [ + "pernah_jualan_seks", + ] + }, + { + "start_y": 500, + "fields": [ + "pernah_homoseks", + ] + }, + { + "start_y": 590, + "fields": [ + "pernah_tinggal_di_eropa", + "pernah_terima_transfusi_di_inggris", + ] + }, + { + "start_y": 730, + "fields": [ + "pernah_tinggal_di_inggris", + ] + }, + { + "start_y": 820, + "fields": [ + "positif_aids", + ] + }, + { + "start_y": 885, + "fields": [ + "pakai_jarum_suntik", + "pakai_konsentrat", + "menderita_hepatitis", + "menderita_malaria", + "menderita_kanker", + "bermasalah_jantung_paru_paru", + "menderita_pendarahan", + "seks_dengan_orang_afrika", + "tinggal_di_afrika", + ] + }, + ] + + for column_with_start_y in second_column_fields_with_start_y: + start_y = column_with_start_y["start_y"] + fields = column_with_start_y["fields"] + for i, field in enumerate(fields): + x = second_column_yes_x if getattr(daftar_donor, field) is True else second_column_no_x + draw.text(xy=(x, start_y + i*delta_y), **kwargs) + + def generate_formulir(self, daftar_donor): + template_data_diri_path = os.path.join(resource_path, "form-a4-data_diri.jpg") + formulir_data_diri = Image.open(template_data_diri_path).convert("RGB") + self._draw_formulir_data_diri(daftar_donor, formulir_data_diri) + + template_kondisi_path = os.path.join(resource_path, "form-a4-kondisi.jpg") + formulir_kondisi = Image.open(template_kondisi_path).convert("RGB") + self._draw_formulir_kondisi(daftar_donor, formulir_kondisi) + + formulir_folder = 'formulir-daftar-donor' + formulir_name = os.path.join(formulir_folder, 'formulir-' + str(daftar_donor.pk) + '.pdf') + formulir_folder_path = os.path.join(settings.MEDIA_ROOT, formulir_folder) + os.makedirs(formulir_folder_path, exist_ok=True) + formulir_path = os.path.join(settings.MEDIA_ROOT, formulir_name) + formulir_data_diri.save(formulir_path, save_all=True, append_images=[formulir_kondisi]) + return settings.MEDIA_URL + formulir_name + + +formulir_daftar_donor_service = FormulirDaftarDonorService() + + +class JadwalDonorSpreadsheetService: + + @classmethod + def _birthdates_to_age(cls, birthdates): + today = datetime.today() + return [relativedelta(today, birthdate).years for birthdate in birthdates] + + @classmethod + def _get_daftar_donor_ulang_list(cls, daftar_donor_qs): + daftar_donor_ulang_list = [] + for daftar_donor in daftar_donor_qs: + daftar_donor_ulang = DaftarDonor.objects.filter( + user=daftar_donor.user, + has_attended=True, + jadwal_donor__time_end__lt=daftar_donor.jadwal_donor.time_start) + daftar_donor_ulang_list.append(daftar_donor_ulang) + + return daftar_donor_ulang_list + + @classmethod + def _get_baru_ulang(cls, daftar_donor_qs): + baru_ulang_list = [] + for daftar_donor in daftar_donor_qs: + daftar_donor_ulang = DaftarDonor.objects.filter( + user=daftar_donor.user, + has_attended=True, + jadwal_donor__time_end__lt=daftar_donor.jadwal_donor.time_start) + baru_ulang_list.append('Ulang' if daftar_donor_ulang.exists() else 'Baru') + + return baru_ulang_list + + @classmethod + def _write_header(cls, writer): + workbook = writer.book + merge_format = workbook.add_format({ + 'align': 'center', + 'valign': 'vcenter', + 'bold': True, + }) + merge_format.set_border(1) + worksheet = writer.sheets['Sheet1'] + + worksheet.merge_range('A1:A3', 'No', merge_format) + worksheet.merge_range('B1:B3', 'NoTrans', merge_format) + worksheet.merge_range('C1:C3', 'Lokasi', merge_format) + worksheet.merge_range('D1:D3', 'Tanggal', merge_format) + worksheet.merge_range('E1:M1', 'Pendonor', merge_format) + headers = 'ID', 'Nama Lengkap', 'Alamat', 'HP', 'Umur', 'Gol (RH)', 'JK', 'Baru Ulang', 'Donor Ke-' + for i, header in enumerate(headers): + col = 4 + i + worksheet.merge_range(1, col, 2, col, header, merge_format) + + @classmethod + def _get_col_widths(cls, df): + # First we find the maximum length of the index column + idx_max = max([len(str(s)) for s in df.index.values] + [len(str(df.index.name))]) + # Then, we concatenate this to the max of the lengths of column name + # and its values for each column, left to right + return [idx_max] + [max([len(str(s)) for s in df[col].values] + [len(col)]) for col in df.columns] + + def download_jadwaldonor(self, modeladmin, queryset): + + response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + response['Content-Disposition'] = f'attachment; filename={modeladmin.model._meta}.xlsx' + + daftar_donor = DaftarDonor.download_query(queryset) + fieldnames = [ + 'id', + 'jadwal_donor__location', + 'jadwal_donor__time_start', + 'user__profile__id_card_no', + 'user__first_name', + 'user__profile__address', + 'user__profile__phone_no', + 'user__profile__birthdate', + 'user__profile__blood_type', + 'user__profile__sex', + ] + df = read_frame(daftar_donor, fieldnames=fieldnames) + df[fieldnames[7]] = self._birthdates_to_age(df[fieldnames[7]]) + daftar_donor_ulang_list = self._get_daftar_donor_ulang_list(daftar_donor) + df['Baru/Ulang'] = [ + 'Ulang' if daftar_donor_ulang.exists() else 'Belum' + for daftar_donor_ulang in daftar_donor_ulang_list + ] + df['Daftar Ke-'] = [ + len(daftar_donor_ulang) + 1 + for daftar_donor_ulang in daftar_donor_ulang_list + ] + + if len(df): + df[fieldnames[2]] = df[fieldnames[2]].dt.tz_localize(None) + + # Not actually used to write headers but still usefull to adjust column widths + df = df.rename(columns={ + fieldnames[0]: 'NoTrans', + fieldnames[1]: 'Lokasi', + fieldnames[2]: 'Tanggal', + fieldnames[3]: 'ID', + fieldnames[4]: 'Nama Lengkap', + fieldnames[5]: 'Alamat', + fieldnames[6]: 'HP', + fieldnames[7]: 'Umur', + fieldnames[8]: 'Gol (RH)', + fieldnames[9]: 'JK', + }) + + writer = pd.ExcelWriter(response, engine='xlsxwriter') + df.to_excel(writer, sheet_name='Sheet1', startrow=3, header=False) + self._write_header(writer) + + workbook = writer.book + worksheet = writer.sheets['Sheet1'] + + text_format = workbook.add_format({'text_wrap': True}) + + for i, width in enumerate(self._get_col_widths(df)): + worksheet.set_column(i, i, width, text_format) + + workbook.close() + return response + + +jadwal_donor_spreadsheet_service = JadwalDonorSpreadsheetService() diff --git a/backend/donor/test_models.py b/backend/donor/test_models.py index 12091279e48bf4c64b570a2718e03a6e6f2db479..0336c66d8db2bd734ce4bc4a6beca69ee1a31919 100644 --- a/backend/donor/test_models.py +++ b/backend/donor/test_models.py @@ -1,9 +1,11 @@ +from datetime import datetime, timedelta from django.test import TestCase +from django.utils import timezone +from django.core.exceptions import ValidationError + from main.factories import UserFactory -from donor.models import JadwalDonor +from donor.models import JadwalDonor, DaftarDonor from donor.factories import JadwalDonorFactory, DaftarDonorFactory -from datetime import datetime -from django.core.exceptions import ValidationError class JadwalDonorTest(TestCase): @@ -15,7 +17,7 @@ class JadwalDonorTest(TestCase): kecamatan="Beji", location='Fasilkom', time_start=datetime.fromisoformat(time_start), time_end=datetime.fromisoformat(time_end), quota=150, category=JadwalDonor.Category.PUBLIC ) - self.assertEqual(str(jadwal_donor), f"Fasilkom, 2020-03-02 (07:08:00 - 10:08:00)") + self.assertEqual(str(jadwal_donor), "Fasilkom, 2020-03-02 (07:08:00 - 10:08:00)") def test_create_jadwal_donor_start_greater_than_end_fail(self): time_start = '2020-03-02T10:08:00+07:00' @@ -35,3 +37,17 @@ class DaftarDonorTest(TestCase): jadwal_donor = JadwalDonorFactory() daftar_donor = DaftarDonorFactory(user=user, jadwal_donor=jadwal_donor) self.assertEqual(str(daftar_donor), str(user) + ' - ' + str(jadwal_donor)) + + def test_download_query(self): + user1 = UserFactory(email='donald@duckduckgo.org') + user1.save() + user2 = UserFactory(email='daisy@duckduckgo.org') + user2.save() + jadwal_donor = JadwalDonorFactory(time_start=timezone.localtime() - timedelta(hours=24), + time_end=timezone.localtime() - timedelta(hours=23)) + DaftarDonorFactory(user=user1, jadwal_donor=jadwal_donor, has_attended=True) + DaftarDonorFactory(user=user2, jadwal_donor=jadwal_donor, has_attended=True) + + qs = DaftarDonor.download_query(JadwalDonor.objects.filter(id=jadwal_donor.id)) + + self.assertEqual(len(qs), 2) diff --git a/backend/donor/tests.py b/backend/donor/tests.py index b5b338bcc03e3211a915af54f00493fedb4ae872..ae1aeeded840738ac83f364671aadac0c3105d42 100644 --- a/backend/donor/tests.py +++ b/backend/donor/tests.py @@ -1,13 +1,16 @@ +from datetime import timedelta +from django.urls import reverse +from django.utils import timezone +from django.utils.timezone import localtime +from django.test import TestCase from rest_framework import status from rest_framework.test import APITestCase -from django.utils import timezone from donor.factories import JadwalDonorFactory, DaftarDonorFactory from donor.models import JadwalDonor from main.factories import UserFactory -from datetime import timedelta +from main.models import User from rest_framework_authlib.tokens import AccessToken -from django.utils.timezone import localtime class JadwalDonorTests(APITestCase): @@ -271,3 +274,45 @@ class RiwayatDonorTest(APITestCase): 'quota': second_jadwal_donor.quota, 'category': second_jadwal_donor.category }]) + + +class JadwalDonorAdminTest(TestCase): + + def setUp(self): + username = 'admin@host' + password = 'secretsauce' + User.objects.create_superuser(username, password) + self.client.login(username=username, password=password) + + def test_action_download_jadwaldonor(self): + user = UserFactory(email='donald@duckduckgo.org') + user.save() + jadwal_donor = JadwalDonorFactory(time_start=timezone.localtime() - timedelta(hours=24), + time_end=timezone.localtime() - timedelta(hours=23)) + DaftarDonorFactory(user=user, jadwal_donor=jadwal_donor, has_attended=True) + + endpoint = reverse('admin:donor_jadwaldonor_changelist') + data = {'action': 'download_jadwaldonor', '_selected_action': [jadwal_donor.id]} + response = self.client.post(endpoint, data=data) + + self.assertEqual(response['content-type'], 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + + +class FormulirDaftarDonorTest(APITestCase): + def setUp(self): + self.user = UserFactory(email='fairuzi@dblood.com') + self.user.save() + self.jadwal_donor = JadwalDonorFactory(time_start=timezone.localtime() - timedelta(hours=24), + time_end=timezone.localtime() - timedelta(hours=23)) + self.daftar_donor = DaftarDonorFactory( + user=self.user, jadwal_donor=self.jadwal_donor, sedang_hamil=True, kehamilan_berapa=3) + self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + str(AccessToken.for_user(self.user))) + + def test_get_formulir_authenticated(self): + self.client.credentials() + response = self.client.get('/donor/formulir-daftar/' + str(self.daftar_donor.id)) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_get_formulir_return_url(self): + response = self.client.get('/donor/formulir-daftar/' + str(self.daftar_donor.id)) + self.assertIn('http', response.data['url']) diff --git a/backend/donor/urls.py b/backend/donor/urls.py index d6095e6905d8c0cbfaabf12f35b75896de4d39c2..81f53a54ae74175cb4c57d74481e98dc1672727d 100644 --- a/backend/donor/urls.py +++ b/backend/donor/urls.py @@ -1,9 +1,10 @@ from django.urls import path -from donor.views import JadwalDonorView, DaftarDonorView, AgendaDonorView, RiwayatDonorView +from donor.views import JadwalDonorView, DaftarDonorView, AgendaDonorView, RiwayatDonorView, FormulirDaftarDonorView urlpatterns = [ path('jadwal/', JadwalDonorView.as_view()), path('jadwal//daftar/', DaftarDonorView.as_view()), path('jadwal/agenda/', AgendaDonorView.as_view()), - path('jadwal/riwayat/', RiwayatDonorView.as_view()) + path('jadwal/riwayat/', RiwayatDonorView.as_view()), + path('formulir-daftar/', FormulirDaftarDonorView.as_view()) ] diff --git a/backend/donor/views.py b/backend/donor/views.py index 06380679c47129c1a21068bfe364abeee8e00122..7905b4a4526248a321394080e6726b3b3260a0e4 100644 --- a/backend/donor/views.py +++ b/backend/donor/views.py @@ -1,13 +1,15 @@ from django.shortcuts import get_object_or_404 from django.utils import timezone from django.utils.timezone import now -from rest_framework import generics +from rest_framework import generics, views from rest_framework.exceptions import ValidationError from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response from donor.models import DaftarDonor, JadwalDonor from donor.serializers import DaftarDonorSerializer, JadwalDonorSerializer from main.paginations import ExtraSmallResultsSetPagination +from donor.services import formulir_daftar_donor_service class JadwalDonorView(generics.ListAPIView): @@ -57,3 +59,14 @@ class RiwayatDonorView(generics.ListAPIView): def get_queryset(self): daftar_donors = DaftarDonor.objects.filter(user=self.request.user, has_attended=True) return JadwalDonor.objects.filter(daftar_donors__in=daftar_donors).order_by('-time_start') + + +class FormulirDaftarDonorView(views.APIView): + + permission_classes = [IsAuthenticated] + + def get(self, request, pk): + daftar_donor = get_object_or_404(DaftarDonor, user=request.user, jadwal_donor__pk=pk) + file_path = formulir_daftar_donor_service.generate_formulir(daftar_donor) + file_url = request.build_absolute_uri(file_path) + return Response({"url": file_url}) diff --git a/backend/edukasi/__init__.py b/backend/edukasi/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/backend/edukasi/admin.py b/backend/edukasi/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..390c3080e16e02183ca8f6f3fd893418552e7e20 --- /dev/null +++ b/backend/edukasi/admin.py @@ -0,0 +1,14 @@ +from django.contrib import admin +from django.conf import settings +from edukasi.models import Artikel + + +@admin.register(Artikel) +class ArtikelAdmin(admin.ModelAdmin): + list_display = ('title', 'posted_at') + search_fields = ('posted_at', 'title') + fields = ('posted_at', 'title', 'featured_image', 'content') + readonly_fields = ('posted_at',) + + class Media: + js = [settings.MEDIA_URL+'/js/artikel_custom_admin.js', ] diff --git a/backend/edukasi/apps.py b/backend/edukasi/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..cf1b53198fcd5964375d673ce743753c0f692a28 --- /dev/null +++ b/backend/edukasi/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class EdukasiConfig(AppConfig): + name = 'edukasi' diff --git a/backend/edukasi/factories.py b/backend/edukasi/factories.py new file mode 100644 index 0000000000000000000000000000000000000000..b20e09f45e2a3ee7158911b35f86aab050adc67a --- /dev/null +++ b/backend/edukasi/factories.py @@ -0,0 +1,17 @@ +import os + +import factory +from faker import Faker + +from edukasi.models import Artikel + +fake = Faker() + + +class ArtikelFactory(factory.DjangoModelFactory): + class Meta: + model = Artikel + + title = fake.sentence()[:-1][:58] + featured_image = os.path.join("edukasi", "placeholder.jpg") + content = "\n".join(fake.paragraphs()) diff --git a/backend/edukasi/management/commands/seed_artikel.py b/backend/edukasi/management/commands/seed_artikel.py new file mode 100644 index 0000000000000000000000000000000000000000..077e1ca679cd0aa1a6322d2a39a41683bc6721ab --- /dev/null +++ b/backend/edukasi/management/commands/seed_artikel.py @@ -0,0 +1,27 @@ +import os +import random + +from django.core.management import BaseCommand +from faker import Faker + +from edukasi.models import Artikel + +fake = Faker() +fake.seed_instance(10) # NOSONAR + + +class Command(BaseCommand): + help = 'Add dummy data artikel edukasi' + + def handle(self, *args, **options): + for _ in range(20): + content = [] + for _ in range(5): + content.append(" ".join(fake.paragraphs())) + Artikel.objects.create( + title=fake.sentence()[:-1][:30], + featured_image=os.path.join("edukasi", random.choice([ # NOSONAR + "placeholder1.jpg", "placeholder2.jpg", "placeholder3.jpg"])), + content="\n".join(content) + ) + self.stdout.write(self.style.SUCCESS("Successfully add dummy data artikel edukasi")) diff --git a/backend/edukasi/migrations/0001_initial.py b/backend/edukasi/migrations/0001_initial.py new file mode 100644 index 0000000000000000000000000000000000000000..00b0cdb158b865bb990928c19f03f94efcbcd071 --- /dev/null +++ b/backend/edukasi/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# Generated by Django 3.0.5 on 2020-05-04 05:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Artikel', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=32)), + ('featured_image', models.ImageField(upload_to='')), + ('content', models.TextField()), + ], + ), + ] diff --git a/backend/edukasi/migrations/0002_artikel_posted_at.py b/backend/edukasi/migrations/0002_artikel_posted_at.py new file mode 100644 index 0000000000000000000000000000000000000000..e8fcff910046e9dfd303a57e7bc132937a00e4e3 --- /dev/null +++ b/backend/edukasi/migrations/0002_artikel_posted_at.py @@ -0,0 +1,20 @@ +# Generated by Django 3.0.5 on 2020-05-04 05:56 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('edukasi', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='artikel', + name='posted_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + ] diff --git a/backend/edukasi/migrations/0003_auto_20200603_2016.py b/backend/edukasi/migrations/0003_auto_20200603_2016.py new file mode 100644 index 0000000000000000000000000000000000000000..2b7c8f3dc3c66fcc20b3128a155cd78a57f37473 --- /dev/null +++ b/backend/edukasi/migrations/0003_auto_20200603_2016.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2020-06-03 13:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('edukasi', '0002_artikel_posted_at'), + ] + + operations = [ + migrations.AlterField( + model_name='artikel', + name='title', + field=models.CharField(max_length=25), + ), + ] diff --git a/backend/edukasi/migrations/0004_auto_20200603_2020.py b/backend/edukasi/migrations/0004_auto_20200603_2020.py new file mode 100644 index 0000000000000000000000000000000000000000..3de5ae4f1579acb8607c2fa750ea09c53c706929 --- /dev/null +++ b/backend/edukasi/migrations/0004_auto_20200603_2020.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2020-06-03 13:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('edukasi', '0003_auto_20200603_2016'), + ] + + operations = [ + migrations.AlterField( + model_name='artikel', + name='title', + field=models.CharField(max_length=30), + ), + ] diff --git a/backend/edukasi/migrations/0005_auto_20200603_2118.py b/backend/edukasi/migrations/0005_auto_20200603_2118.py new file mode 100644 index 0000000000000000000000000000000000000000..6521914e7cbac34683d5f4d51cd45b83fcc8a90d --- /dev/null +++ b/backend/edukasi/migrations/0005_auto_20200603_2118.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2020-06-03 14:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('edukasi', '0004_auto_20200603_2020'), + ] + + operations = [ + migrations.AlterField( + model_name='artikel', + name='title', + field=models.CharField(max_length=60), + ), + ] diff --git a/backend/edukasi/migrations/__init__.py b/backend/edukasi/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/backend/edukasi/models.py b/backend/edukasi/models.py new file mode 100644 index 0000000000000000000000000000000000000000..ee9d8c0b2e735d314719fbf88863eff8b4173baa --- /dev/null +++ b/backend/edukasi/models.py @@ -0,0 +1,14 @@ +from django.db import models + + +class Artikel(models.Model): + title = models.CharField(max_length=60) + featured_image = models.ImageField() + content = models.TextField() + posted_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return "Artikel " + self.title + + class Meta: + pass diff --git a/backend/edukasi/serializers.py b/backend/edukasi/serializers.py new file mode 100644 index 0000000000000000000000000000000000000000..4980d127b4a995f3a1d1a1ec53b0bd7e47e57a02 --- /dev/null +++ b/backend/edukasi/serializers.py @@ -0,0 +1,9 @@ +from rest_framework import serializers + +from .models import Artikel + + +class ArtikelSerializer(serializers.ModelSerializer): + class Meta: + model = Artikel + fields = ['id', 'title', 'featured_image', 'content', 'posted_at'] diff --git a/backend/edukasi/tests.py b/backend/edukasi/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..2a19c260d4351ffeeb441b6b515fb02cced47377 --- /dev/null +++ b/backend/edukasi/tests.py @@ -0,0 +1,30 @@ +from django.test import TestCase +from rest_framework import status +from rest_framework.test import APITestCase + +from .factories import ArtikelFactory + + +class ArtikelTests(TestCase): + def test_create(self): + article = ArtikelFactory(title="Sepuluh Manfaat Donor Darah") + self.assertEqual(str(article), "Artikel Sepuluh Manfaat Donor Darah") + + +class ArtikelViewTests(APITestCase): + def setUp(self): + self.article = ArtikelFactory(title="Sepuluh Manfaat Donor Darah") + + def test_get_artikel_sorted_by_posted_at(self): + new_article = ArtikelFactory() + + response = self.client.get('/edukasi/artikel/') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['results'][0]['title'], new_article.title) + self.assertEqual(response.data['results'][1]['title'], self.article.title) + + def test_retrieve_artikel(self): + response = self.client.get('/edukasi/artikel/' + str(self.article.id) + '/') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['title'], self.article.title) + self.assertEqual(response.data['content'], self.article.content) diff --git a/backend/edukasi/urls.py b/backend/edukasi/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..48f183ce70ca7b245b5c5f43e28ceff099af6689 --- /dev/null +++ b/backend/edukasi/urls.py @@ -0,0 +1,8 @@ +from rest_framework.routers import DefaultRouter + +from edukasi.views import ArtikelViewSet + +router = DefaultRouter() +router.register(r'artikel', ArtikelViewSet, basename='artikel') + +urlpatterns = router.urls diff --git a/backend/edukasi/views.py b/backend/edukasi/views.py new file mode 100644 index 0000000000000000000000000000000000000000..3c7333b48eff3fd5fc33dfc13c48082f1944575a --- /dev/null +++ b/backend/edukasi/views.py @@ -0,0 +1,11 @@ +from rest_framework import viewsets +from .serializers import ArtikelSerializer +from .models import Artikel +from main.paginations import SmallResultsSetPagination + + +class ArtikelViewSet(viewsets.ReadOnlyModelViewSet): + + serializer_class = ArtikelSerializer + pagination_class = SmallResultsSetPagination + queryset = Artikel.objects.all().order_by('-posted_at') diff --git a/backend/main/admin.py b/backend/main/admin.py index ab11241c0b94d2c014b668ef20732c8278ca4292..c5e292ee42ffb8f8857451737bb1ec8d870c04b2 100644 --- a/backend/main/admin.py +++ b/backend/main/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin +from django.conf import settings from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin from django.utils.translation import ugettext_lazy as _ @@ -9,10 +10,17 @@ from .models import User, Profile class UserAdmin(DjangoUserAdmin): """Define admin model for custom User model with no username field.""" + readonly_fields = ('email', + 'first_name', + 'last_name', + 'last_login', + 'date_joined') + fieldsets = ( (None, {'fields': ('email', 'password')}), (_('Personal info'), {'fields': ('first_name', 'last_name')}), - (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'is_verified', + (_('Permissions'), {'fields': ('is_active', 'is_staff', + 'is_superuser', 'is_verified', 'groups', 'user_permissions')}), (_('Important dates'), {'fields': ('last_login', 'date_joined')}), ) @@ -26,7 +34,79 @@ class UserAdmin(DjangoUserAdmin): search_fields = ('email', 'first_name', 'last_name') ordering = ('email',) + class Media: + js = [settings.MEDIA_URL+'/js/user_custom_admin.js', ] + + +class InputFilter(admin.SimpleListFilter): + template = 'admin/input_filter.html' + + def lookups(self, request, model_admin): + # Dummy, required to show the filter. + return ((),) + + def choices(self, changelist): + # Grab only the "all" option. + all_choice = next(super().choices(changelist)) + all_choice['query_parts'] = ( + (k, v) + for k, v in changelist.get_filters_params().items() + if k != self.parameter_name + ) + yield all_choice + @admin.register(Profile) class ProfileAdmin(admin.ModelAdmin): - pass + list_display = ('user', 'district', 'address', 'age', 'sex', 'blood_type') + list_filter = ('blood_type', 'sex') + search_fields = ('address', 'district') + + readonly_fields = ('user', + 'body_weight', + 'id_card_no', + 'birthplace', + 'birthdate', + 'sex', + 'profession', + 'blood_type', + 'married_status', + 'address', + 'city', + 'district', + 'village', + 'phone_no', + 'work_address', + 'work_email', + 'work_phone_no', + 'age') + + fieldsets = ( + (None, {'fields': ('user',)}), + ('INFORMASI PRIBADI', { + 'fields': ('id_card_no', + 'birthplace', + 'birthdate', + 'age', + 'body_weight', + 'sex', + 'profession', + 'blood_type', + 'married_status') + }), + ('ALAMAT', { + 'fields': ('address', + 'village', + 'district', + 'city', + 'work_address') + }), + ('KONTAK', { + 'fields': ('phone_no', + 'work_email', + 'work_phone_no') + }), + ) + + class Media: + js = [settings.MEDIA_URL+'/js/profile_custom_admin.js', ] diff --git a/backend/main/factories.py b/backend/main/factories.py index dba61edcfb59f29037fe233871dc1c8e2ca23866..7f792ee4ea4893aa168493b938e03ed720ebf6f9 100644 --- a/backend/main/factories.py +++ b/backend/main/factories.py @@ -9,7 +9,7 @@ LOCALE = 'id_ID' BLOOD_LETTERS = ('A', 'B', 'O', 'AB') RHESUS = '+-' -BLOOD_TYPES = list(l + r for l, r in itertools.product(BLOOD_LETTERS, RHESUS)) +BLOOD_TYPES = list(m + r for m, r in itertools.product(BLOOD_LETTERS, RHESUS)) class ProfileFactory(factory.Factory): diff --git a/backend/main/management/commands/seed_users.py b/backend/main/management/commands/seed_users.py new file mode 100644 index 0000000000000000000000000000000000000000..9938fe743bde9399b5eb02511a906f329318760a --- /dev/null +++ b/backend/main/management/commands/seed_users.py @@ -0,0 +1,17 @@ +from django.core.management.base import BaseCommand +from ...factories import UserFactory + + +class Command(BaseCommand): + help = 'Seeds 100 users.' + + def add_arguments(self, parser): + parser.add_argument('--size', default=100, type=int) + + def handle(self, *args, **options): + size = abs(options['size']) + for _ in range(size): + u = UserFactory() + u.save() + + self.stdout.write(self.style.SUCCESS(f'Successfully seeds {size} users.')) diff --git a/backend/main/models.py b/backend/main/models.py index 96b6f8ffb23a7b0755af826fa66fb6a84fa1235f..2b1090130e4a150acadda34b66aa59719b4822b5 100644 --- a/backend/main/models.py +++ b/backend/main/models.py @@ -1,3 +1,4 @@ +from datetime import date from django.conf import settings from django.contrib.auth.models import AbstractUser, BaseUserManager from django.db import models @@ -90,8 +91,16 @@ class Profile(models.Model): work_email = models.EmailField(blank=True) work_phone_no = models.CharField(max_length=20, blank=True) + @property + def age(self): + if self.birthdate is None: + return 0 + born = self.birthdate + today = date.today() + return today.year - born.year - ((today.month, today.day) < (born.month, born.day)) + def __str__(self): - return f'({self.sex}, {self.blood_type})' + return f'{self.user} ({self.sex}, {self.blood_type})' class Meta: pass diff --git a/backend/main/paginations.py b/backend/main/paginations.py index e1c6a01d27830c98c77cbf0616ca9966e738a2c8..fecc558a09d5367be6ec7e083c7aa46446a8b88b 100644 --- a/backend/main/paginations.py +++ b/backend/main/paginations.py @@ -3,3 +3,7 @@ from rest_framework.pagination import PageNumberPagination class ExtraSmallResultsSetPagination(PageNumberPagination): page_size = 3 + + +class SmallResultsSetPagination(PageNumberPagination): + page_size = 6 diff --git a/backend/main/signals.py b/backend/main/signals.py index 268278c484b5c50212accd1c9f6eef3735ef61ac..de1bcbfbb6ed4f89a32f9c535c95e8b387dd3f12 100644 --- a/backend/main/signals.py +++ b/backend/main/signals.py @@ -4,6 +4,7 @@ from django.dispatch import receiver from .models import Profile + @receiver(post_save, sender=settings.AUTH_USER_MODEL, dispatch_uid='profile_save_on_create_user_signal') def profile_save_on_create_user_save(sender, instance, **kwargs): diff --git a/backend/main/templates/admin/input_filter.html b/backend/main/templates/admin/input_filter.html new file mode 100644 index 0000000000000000000000000000000000000000..216dded2c9a573d76be1400c6dea8bd38d7f2f9c --- /dev/null +++ b/backend/main/templates/admin/input_filter.html @@ -0,0 +1,24 @@ +{% load i18n %} + +

{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}

+ \ No newline at end of file diff --git a/backend/main/test_commands.py b/backend/main/test_commands.py new file mode 100644 index 0000000000000000000000000000000000000000..994fdb9d1ce669499f3d264febc986512f653582 --- /dev/null +++ b/backend/main/test_commands.py @@ -0,0 +1,14 @@ +from io import StringIO +from django.core.management import call_command +from django.test import TestCase + +from .models import User + + +class SeedUserTest(TestCase): + def test_command_success(self): + out = StringIO() + users_amount = len(User.objects.filter()) + call_command('seed_users', stdout=out) + self.assertIn('Successfully seeds 100 users.', out.getvalue()) + self.assertEqual(users_amount + 100, len(User.objects.filter())) diff --git a/backend/main/test_models.py b/backend/main/test_models.py index 0ea18b1b116b2783eee139d2a995dd03aac6f9ff..13e355f605e74c8acd3d63b5f77d09fbd784adcc 100644 --- a/backend/main/test_models.py +++ b/backend/main/test_models.py @@ -105,7 +105,7 @@ class ProfileTests(TestCase): profile__sex=Sex.FEMALE, profile__blood_type='O+') - self.assertEqual(str(user.profile), '(F, O+)') + self.assertEqual(str(user.profile), f'{str(user)} (F, O+)') def test_dont_save_new_profile_on_user_update(self): user = UserFactory(last_name='Musthofa') diff --git a/backend/main/test_views.py b/backend/main/test_views.py index c24354c1584c2757e04b2819392d03342bcc1409..b5d523c422a533a9441e23de567dfc101d77887d 100644 --- a/backend/main/test_views.py +++ b/backend/main/test_views.py @@ -130,6 +130,30 @@ class AccessTokenAPITestCase(APITestCase): self.assertNotIn('password', response.data) +class AccessRefreshPairTokenAPITestCase(APITestCase): + + def setUp(self): + self.creds = { + 'email': 'donald@duckduckgo.org', + 'password': '5up3r_53cu3r', + } + + User.objects.create_user(**self.creds) + + def test_get_access_token(self): + response = self.client.post('/auth/pair/', data=self.creds) + + self.assertIn('access', response.data) + self.assertIn('refresh', response.data) + self.assertNotIn('password', response.data) + + def test_get_access_token_fail(self): + self.creds['password'] = 'wrong_password' + response = self.client.post('/auth/pair/', data=self.creds) + + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + class UserProfileViewTestCase(APITestCase): def setUp(self): @@ -231,7 +255,15 @@ class OAuthAccessTokenViewTestCase(APITestCase): response = self.client.post('/auth/access/oauth/', data={'tokenId': self.token}) - self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual('access' in response.data, True) + + def test_get_refresh_token(self): + with patch('main.views.OAuthAccessTokenView.get_keys') as fake_keys: + fake_keys.return_value = self.keys + response = self.client.post('/auth/access/oauth/', + data={'tokenId': self.token}) + + self.assertEqual('refresh' in response.data, True) def test_get_access_token_bad(self): with patch('main.views.OAuthAccessTokenView.get_keys') as fake_keys: diff --git a/backend/main/urls.py b/backend/main/urls.py index 0544b14e1595eff3604bd537a449a4c452b36257..2d8305115144ea48d270c4453f87770b7d08977a 100644 --- a/backend/main/urls.py +++ b/backend/main/urls.py @@ -1,5 +1,9 @@ from django.urls import path -from rest_framework_authlib.views import AccessTokenView +from rest_framework_authlib.views import ( + AccessTokenView, + AccessRefreshPairTokenView, + RefreshTokenView, +) from .views import ( ChangePasswordView, @@ -17,6 +21,8 @@ urlpatterns = [ path('auth/access/oauth/', OAuthAccessTokenView.as_view()), path('auth/email-verification/', EmailVerificationView.as_view(), name='email-verification'), + path('auth/pair/', AccessRefreshPairTokenView.as_view()), + path('auth/refresh/', RefreshTokenView.as_view()), path('auth/register-full/', RegisterFullView.as_view()), # Misc PoC path diff --git a/backend/main/utils/numbers.py b/backend/main/utils/numbers.py new file mode 100644 index 0000000000000000000000000000000000000000..5b37b77415374622e439551cab25efa826d1d92b --- /dev/null +++ b/backend/main/utils/numbers.py @@ -0,0 +1,6 @@ +def last_digit(n): + return n % 10 + + +def second_last_digit(n): + return n//10 % 10 diff --git a/backend/main/utils/python.py b/backend/main/utils/python.py new file mode 100644 index 0000000000000000000000000000000000000000..e001999f98896edd1ce5d6efa8cf215e2b19cef8 --- /dev/null +++ b/backend/main/utils/python.py @@ -0,0 +1,5 @@ +def default_kwargs_with_override(default, override): + result = default.copy() + for key in override: + result[key] = override[key] + return result diff --git a/backend/main/views.py b/backend/main/views.py index 304ac78f584a04056e584ed29a5a99f42c43a063..9745ee1c6742378057f721e616d174e86ebe4143 100644 --- a/backend/main/views.py +++ b/backend/main/views.py @@ -9,7 +9,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import exceptions, generics, status, views from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from rest_framework_authlib.tokens import AccessToken +from rest_framework_authlib.tokens import AccessToken, RefreshToken from urllib.parse import urljoin, quote from . import ( @@ -163,7 +163,8 @@ class OAuthAccessTokenView(views.APIView): is_verified=True) data = { - 'access': str(AccessToken.for_user(user)) + 'access': str(AccessToken.for_user(user)), + 'refresh': str(RefreshToken.for_user(user)), } return Response(data) diff --git a/backend/media/edukasi/placeholder1.jpg b/backend/media/edukasi/placeholder1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6186dfeba3891f539cad75a9d458357a6a842521 Binary files /dev/null and b/backend/media/edukasi/placeholder1.jpg differ diff --git a/backend/media/edukasi/placeholder2.jpg b/backend/media/edukasi/placeholder2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..358c4fe194fb6583c4262b26f53682bd86eb203d Binary files /dev/null and b/backend/media/edukasi/placeholder2.jpg differ diff --git a/backend/media/edukasi/placeholder3.jpg b/backend/media/edukasi/placeholder3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..593ab0cfc737802c6039e7d5a1d395afc2041ffc Binary files /dev/null and b/backend/media/edukasi/placeholder3.jpg differ diff --git a/backend/media/formulir-daftar-donor/formulir-1.pdf b/backend/media/formulir-daftar-donor/formulir-1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ac70217d867c98e686deaaf496e6ce3dccf71ed5 Binary files /dev/null and b/backend/media/formulir-daftar-donor/formulir-1.pdf differ diff --git a/backend/requirements-dev.txt b/backend/requirements-dev.txt index db9e660c3bc255a65c0de2fbdf0b042d86bf4a3b..d9e25ae6b5b733817bfe96f3d5c9071cc1f45245 100644 --- a/backend/requirements-dev.txt +++ b/backend/requirements-dev.txt @@ -4,6 +4,6 @@ coverage==5.0.3 django-extensions==2.2.8 factory_boy==2.12.0 -flake8-django==0.0.4 +flake8-django==1.0.0 psycopg2-binary==2.8.4 Faker==4.0.1 diff --git a/backend/requirements.txt b/backend/requirements.txt index 3ec85a0dfb9df08e3e58200842af8758ed1fd9e3..0e2f5ccf95cfbf1e525c0ef6171151209f724a2f 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -6,4 +6,10 @@ Django==3.0.5 django-anymail==7.0.0 djangorestframework==3.11.0 django-cors-headers==3.2.1 +django-kronos==1.0 +django-pandas==0.6.2 gunicorn==20.0.4 +numpy==1.18.4 +pandas==1.0.4 +Pillow==7.1.2 +XlsxWriter==1.2.9 diff --git a/backend/rest_framework_authlib/serializers.py b/backend/rest_framework_authlib/serializers.py index 8375bd77a666c344eb376c1de218df2b51571eb5..2317779c6f8990284387f564fc12e90a8c1f637a 100644 --- a/backend/rest_framework_authlib/serializers.py +++ b/backend/rest_framework_authlib/serializers.py @@ -2,7 +2,9 @@ from django.contrib.auth import authenticate, get_user_model from django.utils.translation import gettext_lazy as _ from rest_framework import exceptions, serializers -from .tokens import AccessToken +from .errors import TokenError, InvalidToken +from .tokens import AccessToken, RefreshToken +from .utils import aware_utcnow User = get_user_model() @@ -73,3 +75,46 @@ class AccessTokenSerializer(CredentialSerializer): data['access'] = str(self.get_token(self._user)) return data + + +class AccessRefreshPairTokenSerializer(CredentialSerializer): + access = serializers.CharField(read_only=True) + refresh = serializers.CharField(read_only=True) + + @classmethod + def get_token(cls, user): + return RefreshToken.for_user(user) + + def validate(self, data): + data = super().validate(data) + + refresh_token = self.get_token(self._user) + + data['refresh'] = str(refresh_token) + data['access'] = str(refresh_token.get_access_token()) + + return data + + +class RefreshTokenSerializer(serializers.Serializer): + refresh = serializers.CharField(required=True) + access = serializers.CharField(read_only=True) + + def validate_refresh(self, value): + try: + self._token = RefreshToken(value, validate=True) + except InvalidToken as err: + raise exceptions.AuthenticationFailed(err.detail, code=err.status_code) + except TokenError as err: + raise exceptions.AuthenticationFailed(str(err), code='bad_token') + + self._token.set_exp(from_time=aware_utcnow()) + + return value + + def validate(self, data): + if hasattr(self, '_token'): + data['access'] = str(self._token.get_access_token()) + data['refresh'] = str(self._token) + + return data diff --git a/backend/rest_framework_authlib/settings.py b/backend/rest_framework_authlib/settings.py index 6ea24b5c707413ef4cfc88908d4e8f365bfd51f9..5e4cb33ddd80dfa4f7e810545950f9b988efcdb8 100644 --- a/backend/rest_framework_authlib/settings.py +++ b/backend/rest_framework_authlib/settings.py @@ -4,7 +4,7 @@ from django.conf import settings settings.REST_FRAMEWORK_AUTHLIB = { 'ACCESS_TOKEN_LIFETIME': timedelta(hours=1), - # 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # 'ROTATE_REFRESH_TOKENS': False, # 'BLACKLIST_AFTER_ROTATION': True, diff --git a/backend/rest_framework_authlib/test_urls.py b/backend/rest_framework_authlib/test_urls.py index f64e69e55aa868a762aded228863164c4f7b719e..439390979cb4a3d67bd2af9f8242399c83d0d1e4 100644 --- a/backend/rest_framework_authlib/test_urls.py +++ b/backend/rest_framework_authlib/test_urls.py @@ -1,7 +1,13 @@ from django.urls import path -from .views import AccessTokenView +from .views import ( + AccessTokenView, + AccessRefreshPairTokenView, + RefreshTokenView, +) urlpatterns = [ path('access', AccessTokenView.as_view()), + path('pair', AccessRefreshPairTokenView.as_view()), + path('refresh', RefreshTokenView.as_view()), ] diff --git a/backend/rest_framework_authlib/test_views.py b/backend/rest_framework_authlib/test_views.py index 467b6b46aa74f872d4d4dc8d734d0fd2974ff20d..658fe906cd73492e26d4a28d425bf5db20cde4e5 100644 --- a/backend/rest_framework_authlib/test_views.py +++ b/backend/rest_framework_authlib/test_views.py @@ -1,12 +1,16 @@ from django.contrib.auth import get_user_model from django.test import override_settings +from rest_framework import status from rest_framework.test import APITestCase +from .tokens import AccessToken, RefreshToken + User = get_user_model() @override_settings(ROOT_URLCONF='rest_framework_authlib.test_urls') class AccessTokenViewTestCase(APITestCase): + def setUp(self): self.username = 'donald@duckduckgo.org' self.password = 'super_secuer' @@ -56,3 +60,109 @@ class AccessTokenViewTestCase(APITestCase): self.assertEqual(res.status_code, 200) self.assertIn('access', res.data) self.assertNotIn('password', res.data) + + +@override_settings(ROOT_URLCONF='rest_framework_authlib.test_urls') +class AccessRefreshPairTokenViewTestCase(APITestCase): + def setUp(self): + self.username = 'donald@duckduckgo.org' + self.password = 'super_secuer' + self.user = User.objects.create_user( + self.username, password=self.password) + + def test_fields_missing(self): + res = self.client.post('/pair', data={}) + self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn(User.USERNAME_FIELD, res.data) + self.assertIn('password', res.data) + + res = self.client.post('/pair', + data={User.USERNAME_FIELD: self.username}) + self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn('password', res.data) + + res = self.client.post('/pair', data={'password': self.password}) + self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn(User.USERNAME_FIELD, res.data) + + def test_credentials_wrong(self): + res = self.client.post('/pair', data={ + User.USERNAME_FIELD: self.username, + 'password': 'test_user', + }) + self.assertEqual(res.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertIn('detail', res.data) + + def test_user_inactive(self): + self.user.is_active = False + self.user.save() + + res = self.client.post('/pair', data={ + User.USERNAME_FIELD: self.username, + 'password': self.password, + }) + self.assertEqual(res.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertIn('detail', res.data) + + def test_success(self): + res = self.client.post('/pair', data={ + User.USERNAME_FIELD: self.username, + 'password': self.password, + }) + + self.assertEqual(res.status_code, status.HTTP_200_OK) + + self.assertIn('access', res.data) + AccessToken(res.data['access'], validate=True) + self.assertIn('refresh', res.data) + RefreshToken(res.data['refresh'], validate=True) + + self.assertNotIn('password', res.data) + + +@override_settings(ROOT_URLCONF='rest_framework_authlib.test_urls') +class RefreshTokenAPIViewTestCase(APITestCase): + + def setUp(self): + self.username = 'donald@duckduckgo.org' + self.password = 'super_secuer' + self.user = User.objects.create_user( + self.username, password=self.password) + self.refresh = RefreshToken.for_user(self.user) + + def test_no_token(self): + response = self.client.post('/refresh', data={}) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_bad_token(self): + response = self.client.post('/refresh', data={ + 'refresh': 'eyeyeyey', + }) + + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_wrong_token_schema(self): + self.refresh.claims['token_schema'] = 'access' + response = self.client.post('/refresh', data={ + 'refresh': str(self.refresh), + }) + + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_expired_token(self): + import datetime + self.refresh.set_exp(from_time=datetime.datetime(2001, 9, 11)) + response = self.client.post('/refresh', data={ + 'refresh': str(self.refresh) + }) + + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_success(self): + response = self.client.post('/refresh', data={ + 'refresh': str(self.refresh), + }) + + self.assertIn('access', response.data) + self.assertIn('refresh', response.data) diff --git a/backend/rest_framework_authlib/tokens.py b/backend/rest_framework_authlib/tokens.py index 3ca684efb20c3f58f2ec806fa02a12d05e637635..9b8f7666a7250151211ae977afa952e27d80c0ca 100644 --- a/backend/rest_framework_authlib/tokens.py +++ b/backend/rest_framework_authlib/tokens.py @@ -10,6 +10,7 @@ from .errors import ( from .settings import ( ACCESS_TOKEN_LIFETIME, ALGORITHMS, + REFRESH_TOKEN_LIFETIME, SIGNING_KEY, TOKEN_SCHEMA_CLAIM, USER_ID_CLAIM, @@ -123,11 +124,13 @@ class TokenBase(JWTClaims): iat = datetime_to_epoch(self.current_time) self.claims['iat'] = iat - def set_exp(self, lifetime=None): + def set_exp(self, from_time=None, lifetime=None): if lifetime is None: lifetime = self.lifetime + if from_time is None: + from_time = self.current_time - exp = datetime_to_epoch(self.current_time + lifetime) + exp = datetime_to_epoch(from_time + lifetime) self.claims['exp'] = exp def set_jti(self): @@ -150,6 +153,44 @@ class AccessToken(TokenBase): lifetime = ACCESS_TOKEN_LIFETIME +class RefreshToken(TokenBase): + token_schema = 'refresh' + lifetime = REFRESH_TOKEN_LIFETIME + no_copy_claims = ( + TOKEN_SCHEMA_CLAIM, + 'exp', + + # Both of these claims are included even though they may be the same. + # It seems possible that a third party token might have a custom or + # namespaced JTI claim as well as a default "jti" claim. In that case, + # we wouldn't want to copy either one. + # api_settings.JTI_CLAIM, + 'jti', + ) + + def get_access_token(self): + """ + Returns an access token created from this refresh token. Copies all + claims present in this refresh token to the new access token except + those claims listed in the `no_copy_claims` attribute. + """ + access = AccessToken() + + # Use instantiation time of refresh token as relative timestamp for + # access token "exp" claim. This ensures that both a refresh and + # access token expire relative to the same time if they are created as + # a pair. + access.set_exp(from_time=self.current_time) + + no_copy = self.no_copy_claims + for claim, value in self.claims.items(): + if claim in no_copy: + continue + access[claim] = value + + return access + + class SlidingToken(TokenBase): token_schema = 'sliding' lifetime = ACCESS_TOKEN_LIFETIME diff --git a/backend/rest_framework_authlib/views.py b/backend/rest_framework_authlib/views.py index 18456faaf97d7942c913e8376701adec44d0b4f4..23321dd8517fbb55bbed117bbd2c8d0a235ed46d 100644 --- a/backend/rest_framework_authlib/views.py +++ b/backend/rest_framework_authlib/views.py @@ -2,7 +2,11 @@ from rest_framework import generics, status from rest_framework.response import Response from .errors import InvalidToken, TokenError -from .serializers import AccessTokenSerializer +from .serializers import ( + AccessTokenSerializer, + AccessRefreshPairTokenSerializer, + RefreshTokenSerializer, +) from .settings import AUTH_HEADER_TYPES @@ -33,3 +37,11 @@ class TokenViewBase(generics.GenericAPIView): class AccessTokenView(TokenViewBase): serializer_class = AccessTokenSerializer + + +class AccessRefreshPairTokenView(TokenViewBase): + serializer_class = AccessRefreshPairTokenSerializer + + +class RefreshTokenView(TokenViewBase): + serializer_class = RefreshTokenSerializer diff --git a/backend/sonar-project.properties b/backend/sonar-project.properties index ddd0a16e2c48c6b484da34d41433350564b30a72..dad74fbb7ac09d04e09d7395a575ad0744095b41 100644 --- a/backend/sonar-project.properties +++ b/backend/sonar-project.properties @@ -1,4 +1,5 @@ sonar.sources=. -sonar.exclusions=dblood/**/*,manage.py,**/apps.py,**/filters.py +sonar.exclusions=dblood/**/*,manage.py sonar.test.inclusions=**/test*.py sonar.python.coverage.reportPaths=coverage.xml +sonar.coverage.exclusions=**/apps.py,**/admin.py \ No newline at end of file diff --git a/backend/stok_darah/factories.py b/backend/stok_darah/factories.py index d62e709a142869ccbf041f4fcf9aba3c695809d2..838f507ed079b1dd1d86e8938bf8f66bbbdd14b4 100644 --- a/backend/stok_darah/factories.py +++ b/backend/stok_darah/factories.py @@ -1,4 +1,6 @@ import factory +import factory.fuzzy + from .models import Darah diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 898c0b2ba350cd5a0785f39e815d7f8e6b56b702..9c0f29945cd02e35efa147c9b99a342366526878 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -11,6 +11,8 @@ services: - "8000:8000" # will be served with "master" NGINX in VM depends_on: - api + volumes: + - .data/media:/var/www/html/staging/api/media/ networks: - default - internal @@ -22,11 +24,16 @@ services: restart: always environment: # - SECRET_KEY=another_totally_secret_random_bytes_no_one_can_touch # There's a fallback + - USE_X_FORWARDED_HOST=True + - MEDIA_URL=/staging/api/media/ + - MEDIA_ROOT=/var/www/html/staging/api/media/ - DATABASE_URL=postgres://mantan:aab@db/dblood # You can try with postgres too ports: - "8080:8000" depends_on: - db + volumes: + - .data/media:/var/www/html/staging/api/media/ networks: - default - internal diff --git a/docker-compose.yml b/docker-compose.yml index 546a3d1d76500d103c94be2a4f36c5b2e32edb79..7ea52d70234381d90b99eb67ac4de1b3220b1e00 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,8 @@ services: - "8001:8000" # will be served with "master" NGINX in VM depends_on: - api + volumes: + - .data/media:/var/www/html/api/media networks: - default - internal @@ -23,6 +25,8 @@ services: # - STATIC_URL=https://dblood-api.netlify.com/ # This is minor setting so it doesnt appear in the Dockerfile - ALLOWED_HOSTS=localhost;ppl2020c6.cs.ui.ac.id # Same as above - USE_X_FORWARDED_HOST=True + - MEDIA_URL=/api/media/ + - MEDIA_ROOT=/var/www/html/api/media/ - SECRET_KEY=another_totally_secret_random_bytes_no_one_can_touch # Required - DATABASE_URL=postgres://mantan:aab@db/dblood # Will default to sqlite unless provided # You can change the settings module, but please be aware that no @@ -30,6 +34,10 @@ services: # - DJANGO_SETTINGS_MODULE=dblood.settings.production ports: - "8081:8000" + depends_on: + - db + volumes: + - .data/media:/var/www/html/api/media networks: - default - internal diff --git a/frontend/.gitignore b/frontend/.gitignore index 756749ff33dfa9e4cffcb6fb52ee762f77bddfbc..31cab422425dd27eb7d9cbff2927ab8a1b1e9005 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -1,3 +1,5 @@ +.vscode/ + # Logs logs *.log diff --git a/frontend/gatsby-node.js b/frontend/gatsby-node.js index 2f4266513eb6606ea0680779ddc146e859d269a3..e1a9977b7d981441351174ff03430d88f33a4e95 100644 --- a/frontend/gatsby-node.js +++ b/frontend/gatsby-node.js @@ -1,7 +1,12 @@ -/** - * Implement Gatsby's Node APIs in this file. - * - * See: https://www.gatsbyjs.org/docs/node-apis/ - */ +exports.onCreatePage = async ({ page, actions }) => { + const { createPage } = actions -// You can delete this file if you're not using it + // page.matchPath is a special key that's used for matching pages + // only on the client. + if (page.path.match(/^\/article/)) { + page.matchPath = "/article/*" + + // Update the page. + createPage(page) + } +} diff --git a/frontend/jest.setup.js b/frontend/jest.setup.js index 85e3892e7bf3aaaa28499af79ac3c517feb864de..263d3b07b374b1bcdc31ca969e095ef61b198e4f 100644 --- a/frontend/jest.setup.js +++ b/frontend/jest.setup.js @@ -1,6 +1,8 @@ import "@testing-library/jest-dom/extend-expect" import moment from "moment" import "moment/locale/id" +import axios from "axios" +import { postRefreshToken } from "./src/api" jest.mock("./src/components/seo") jest.mock("./src/api") @@ -52,3 +54,10 @@ console.warn = jest.fn() moment.locale("id") jest.mock("react-apexcharts") + +postRefreshToken.mockResolvedValue({ + status: 400, +}) +global.beforeEach(() => { + axios.defaults.headers.common = {} +}) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index de75db14a8d6028d71d5b2d717c5ba30d158cd30..9848edb1c1113ae28c75f48593becff4b52d5e21 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -304,6 +304,11 @@ "@babel/types": "^7.8.3" } }, + "@babel/helper-validator-identifier": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", + "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==" + }, "@babel/helper-wrap-function": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", @@ -854,12 +859,19 @@ } }, "@babel/polyfill": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.8.3.tgz", - "integrity": "sha512-0QEgn2zkCzqGIkSWWAEmvxD7e00Nm9asTtQvi7HdlYvMhjy/J38V/1Y9ode0zEJeIuxAI0uftiAzqc7nVeWUGg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.10.1.tgz", + "integrity": "sha512-TviueJ4PBW5p48ra8IMtLXVkDucrlOZAIZ+EXqS3Ot4eukHbWiqcn7DcqpA1k5PcKtmJ4Xl9xwdv6yQvvcA+3g==", "requires": { "core-js": "^2.6.5", - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + } } }, "@babel/preset-env": { @@ -1041,6 +1053,24 @@ "minimist": "^1.2.0" } }, + "@date-io/core": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@date-io/core/-/core-1.3.13.tgz", + "integrity": "sha512-AlEKV7TxjeK+jxWVKcCFrfYAk8spX9aCyiToFIiLPtfQbsjmRGLIhb5VZgptQcJdHtLXo7+m0DuurwFgUToQuA==" + }, + "@date-io/moment": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@date-io/moment/-/moment-1.3.13.tgz", + "integrity": "sha512-3kJYusJtQuOIxq6byZlzAHoW/18iExJer9qfRF5DyyzdAk074seTuJfdofjz4RFfTd/Idk8WylOQpWtERqvFuQ==", + "requires": { + "@date-io/core": "^1.3.13" + } + }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, "@fortawesome/fontawesome-common-types": { "version": "0.2.27", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.27.tgz", @@ -1510,6 +1540,124 @@ } } }, + "@material-ui/core": { + "version": "4.9.13", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.9.13.tgz", + "integrity": "sha512-GEXNwUr+laZ0N+F1efmHB64Fyg+uQIRXLqbSejg3ebSXgLYNpIjnMOPRfWdu4rICq0dAIgvvNXGkKDMcf3AMpA==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/react-transition-group": "^4.3.0", + "@material-ui/styles": "^4.9.13", + "@material-ui/system": "^4.9.13", + "@material-ui/types": "^5.0.1", + "@material-ui/utils": "^4.9.12", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "^1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0", + "react-transition-group": "^4.3.0" + } + }, + "@material-ui/pickers": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@material-ui/pickers/-/pickers-3.2.10.tgz", + "integrity": "sha512-B8G6Obn5S3RCl7hwahkQj9sKUapwXWFjiaz/Bsw1fhYFdNMnDUolRiWQSoKPb1/oKe37Dtfszoywi1Ynbo3y8w==", + "requires": { + "@babel/runtime": "^7.6.0", + "@date-io/core": "1.x", + "@types/styled-jsx": "^2.2.8", + "clsx": "^1.0.2", + "react-transition-group": "^4.0.0", + "rifm": "^0.7.0" + } + }, + "@material-ui/react-transition-group": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@material-ui/react-transition-group/-/react-transition-group-4.3.0.tgz", + "integrity": "sha512-CwQ0aXrlUynUTY6sh3UvKuvye1o92en20VGAs6TORnSxUYeRmkX8YeTUN3lAkGiBX1z222FxLFO36WWh6q73rQ==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "dependencies": { + "dom-helpers": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz", + "integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^2.6.7" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", + "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + } + } + }, + "@material-ui/styles": { + "version": "4.9.13", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.13.tgz", + "integrity": "sha512-lWlXJanBdHQ18jW/yphedRokHcvZD1GdGzUF/wQxKDsHwDDfO45ZkAxuSBI202dG+r1Ph483Z3pFykO2obeSRA==", + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "^5.0.1", + "@material-ui/utils": "^4.9.6", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.0.3", + "jss-plugin-camel-case": "^10.0.3", + "jss-plugin-default-unit": "^10.0.3", + "jss-plugin-global": "^10.0.3", + "jss-plugin-nested": "^10.0.3", + "jss-plugin-props-sort": "^10.0.3", + "jss-plugin-rule-value-function": "^10.0.3", + "jss-plugin-vendor-prefixer": "^10.0.3", + "prop-types": "^15.7.2" + } + }, + "@material-ui/system": { + "version": "4.9.13", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.13.tgz", + "integrity": "sha512-6AlpvdW6KJJ5bF1Xo2OD13sCN8k+nlL36412/bWnWZOKIfIMo/Lb8c8d1DOIaT/RKWxTEUaWnKZjabVnA3eZjA==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.9.6", + "prop-types": "^15.7.2" + } + }, + "@material-ui/types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.0.1.tgz", + "integrity": "sha512-wURPSY7/3+MAtng3i26g+WKwwNE3HEeqa/trDBR5+zWKmcjO+u9t7Npu/J1r+3dmIa/OeziN9D/18IrBKvKffw==" + }, + "@material-ui/utils": { + "version": "4.9.12", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.9.12.tgz", + "integrity": "sha512-/0rgZPEOcZq5CFA4+4n6Q6zk7fi8skHhH2Bcra8R3epoJEYy5PL55LuMazPtPH1oKeRausDV/Omz4BbgFsn1HQ==", + "requires": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0" + } + }, "@mikaelkristiansson/domready": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@mikaelkristiansson/domready/-/domready-1.0.10.tgz", @@ -1550,15 +1698,14 @@ } }, "@reach/router": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@reach/router/-/router-1.2.1.tgz", - "integrity": "sha512-kTaX08X4g27tzIFQGRukaHmNbtMYDS3LEWIS8+l6OayGIw6Oyo1HIF/JzeuR2FoF9z6oV+x/wJSVSq4v8tcUGQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@reach/router/-/router-1.3.3.tgz", + "integrity": "sha512-gOIAiFhWdiVGSVjukKeNKkCRBLmnORoTPyBihI/jLunICPgxdP30DroAvPQuf1eVfQbfGJQDJkwhJXsNPMnVWw==", "requires": { - "create-react-context": "^0.2.1", + "create-react-context": "0.3.0", "invariant": "^2.2.3", "prop-types": "^15.6.1", - "react-lifecycles-compat": "^3.0.4", - "warning": "^3.0.0" + "react-lifecycles-compat": "^3.0.4" } }, "@restart/context": { @@ -1842,9 +1989,9 @@ } }, "@types/history": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.4.tgz", - "integrity": "sha512-+o2igcuZA3xtOoFH56s+MCZVidwlJNcJID57DSCyawS2i910yG9vkwehCjJNZ6ImhCR5S9DbvIJKyYHcMyOfMw==" + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.6.tgz", + "integrity": "sha512-GRTZLeLJ8ia00ZH8mxMO8t0aC9M1N9bN461Z2eaRurJo6Fpa+utgCwLzI4jQHcrdzuzp5WPN9jRwpsCQ1VhJ5w==" }, "@types/istanbul-lib-coverage": { "version": "2.0.1", @@ -1926,14 +2073,14 @@ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, "@types/q": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", - "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" }, "@types/reach__router": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.2.6.tgz", - "integrity": "sha512-Oh5DAVr/L2svBvubw6QEFpXGu295Y406BPs4i9t1n2pp7M+q3pmCmhzb9oZV5wncR41KCD3NHl1Yhi7uKnTPsA==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.5.tgz", + "integrity": "sha512-h0NbqXN/tJuBY/xggZSej1SKQEstbHO7J/omt1tYoFGmj3YXOodZKbbqD4mNDh7zvEGYd7YFrac1LTtAr3xsYQ==", "requires": { "@types/history": "*", "@types/react": "*" @@ -1966,12 +2113,28 @@ "@types/react": "*" } }, + "@types/react-transition-group": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.4.tgz", + "integrity": "sha512-8DMUaDqh0S70TjkqU0DxOu80tFUiiaS9rxkWip/nb7gtvAsbqOXm02UCmR8zdcjWujgeYPiPNTVpVpKzUDotwA==", + "requires": { + "@types/react": "*" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", "dev": true }, + "@types/styled-jsx": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@types/styled-jsx/-/styled-jsx-2.2.8.tgz", + "integrity": "sha512-Yjye9VwMdYeXfS71ihueWRSxrruuXTwKCbzue4+5b2rjnQ//AtyM7myZ1BEhNhBQ/nL/RE7bdToUoLln2miKvg==", + "requires": { + "@types/react": "*" + } + }, "@types/testing-library__dom": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-7.0.1.tgz", @@ -2029,6 +2192,12 @@ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==" }, + "@types/yoga-layout": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/yoga-layout/-/yoga-layout-1.9.2.tgz", + "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==", + "optional": true + }, "@typescript-eslint/eslint-plugin": { "version": "2.22.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.22.0.tgz", @@ -2450,9 +2619,9 @@ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "arch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz", - "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz", + "integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==" }, "argparse": { "version": "1.0.10", @@ -2562,11 +2731,6 @@ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", "optional": true }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -2584,6 +2748,13 @@ "bn.js": "^4.0.0", "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } } }, "assert": { @@ -2658,34 +2829,45 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "auto-bind": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-3.0.0.tgz", - "integrity": "sha512-v0A231a/lfOo6kxQtmEkdBfTApvC21aJYukA8pkKnoTvVqh3Wmm7/Rwy4GBCHTTHVoLVA5qsBDDvf1XY1nIV2g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", + "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", "optional": true }, "autoprefixer": { - "version": "9.7.4", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.4.tgz", - "integrity": "sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g==", + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.0.tgz", + "integrity": "sha512-D96ZiIHXbDmU02dBaemyAg53ez+6F5yZmapmgKcjm35yEe1uVDYI8hGW3VYoGRaG290ZFf91YxHrR518vC0u/A==", "requires": { - "browserslist": "^4.8.3", - "caniuse-lite": "^1.0.30001020", + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001061", "chalk": "^2.4.2", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", - "postcss": "^7.0.26", - "postcss-value-parser": "^4.0.2" + "postcss": "^7.0.30", + "postcss-value-parser": "^4.1.0" }, "dependencies": { "browserslist": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.5.tgz", - "integrity": "sha512-4LMHuicxkabIB+n9874jZX/az1IaZ5a+EUuvD7KFOu9x/Bd5YHyO0DIz2ls/Kl8g0ItS4X/ilEgf4T1Br0lgSg==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", "requires": { - "caniuse-lite": "^1.0.30001022", - "electron-to-chromium": "^1.3.338", - "node-releases": "^1.1.46" + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" } + }, + "caniuse-lite": { + "version": "1.0.30001077", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz", + "integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==" + }, + "node-releases": { + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==" } } }, @@ -2860,52 +3042,6 @@ "mkdirp": "^0.5.3", "pify": "^4.0.1", "schema-utils": "^2.6.5" - }, - "dependencies": { - "ajv": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.1.tgz", - "integrity": "sha512-AUh2mDlJDAnzSRaKkMHopTD1GKwC1ApUq8oCzdjAOM5tavncgqWU+JoRu5Y3iYY0Q/euiU+1LWp0/O/QY8CcHw==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "opencollective-postinstall": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "schema-utils": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz", - "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==", - "requires": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" - } - } } }, "babel-plugin-add-module-exports": { @@ -2985,9 +3121,9 @@ } }, "babel-plugin-remove-graphql-queries": { - "version": "2.7.22", - "resolved": "https://registry.npmjs.org/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-2.7.22.tgz", - "integrity": "sha512-gb4bKZvePZME/VRBMiIZfnli1BIO0K8cm1Pj9HPm85PqElPHzdjeUZ9p3ybOCGe+BBpIlqKx7mx9V/RsjlH+SA==" + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-2.9.3.tgz", + "integrity": "sha512-EfMoizTX4/aUVN/cbWCU+uythWT5Xjh29npZnyTwBL2b16JH7WM9vbVMJQoCi+26HfRpKJS6SJfDcUT12wc3Mg==" }, "babel-plugin-syntax-object-rest-spread": { "version": "6.13.0", @@ -3227,9 +3363,9 @@ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==" }, "body-parser": { "version": "1.19.0", @@ -3502,20 +3638,46 @@ "requires": { "bn.js": "^4.1.0", "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } } }, "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz", + "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.2", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "browserify-zlib": { @@ -3665,11 +3827,12 @@ } }, "cache-manager": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-2.10.1.tgz", - "integrity": "sha512-bk17v9IkLqNcbCzggEh82LEJhjHp+COnL57L7a0ESbM/cOuXIIBatdVjD/ps7vOsofI48++zAC14Ye+8v50flg==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-2.11.1.tgz", + "integrity": "sha512-XhUuc9eYwkzpK89iNewFwtvcDYMUsvtwzHeyEOPJna/WsVsXcrzsA1ft2M0QqPNunEzLhNCYPo05tEfG+YuNow==", "requires": { "async": "1.5.2", + "lodash.clonedeep": "4.5.0", "lru-cache": "4.0.0" } }, @@ -3766,14 +3929,27 @@ }, "dependencies": { "browserslist": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.5.tgz", - "integrity": "sha512-4LMHuicxkabIB+n9874jZX/az1IaZ5a+EUuvD7KFOu9x/Bd5YHyO0DIz2ls/Kl8g0ItS4X/ilEgf4T1Br0lgSg==", - "requires": { - "caniuse-lite": "^1.0.30001022", - "electron-to-chromium": "^1.3.338", - "node-releases": "^1.1.46" + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", + "requires": { + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" + }, + "dependencies": { + "caniuse-lite": { + "version": "1.0.30001077", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz", + "integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==" + } } + }, + "node-releases": { + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==" } } }, @@ -4126,12 +4302,13 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" }, "clipboardy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.1.0.tgz", - "integrity": "sha512-2pzOUxWcLlXWtn+Jd6js3o12TysNOOVes/aQfg+MT/35vrxWzedHlLwyoJpXjsFKWm95BTNEcMGD9+a7mKzZkQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz", + "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", "requires": { "arch": "^2.1.1", - "execa": "^1.0.0" + "execa": "^1.0.0", + "is-wsl": "^2.1.1" }, "dependencies": { "execa": { @@ -4193,6 +4370,11 @@ "mimic-response": "^1.0.0" } }, + "clsx": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.0.tgz", + "integrity": "sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -4276,9 +4458,9 @@ } }, "command-exists": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.8.tgz", - "integrity": "sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw==" + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" }, "commander": { "version": "2.20.3", @@ -4489,16 +4671,23 @@ } }, "copyfiles": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.2.0.tgz", - "integrity": "sha512-iJbHJI+8OKqsq+4JF0rqgRkZzo++jqO6Wf4FUU1JM41cJF6JcY5968XyF4tm3Kkm7ZOMrqlljdm8N9oyY5raGw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.3.0.tgz", + "integrity": "sha512-73v7KFuDFJ/ofkQjZBMjMBFWGgkS76DzXvBMUh7djsMOE5EELWtAO/hRB6Wr5Vj5Zg+YozvoHemv0vnXpqxmOQ==", "requires": { "glob": "^7.0.5", "minimatch": "^3.0.3", - "mkdirp": "^0.5.1", + "mkdirp": "^1.0.4", "noms": "0.0.0", "through2": "^2.0.1", - "yargs": "^13.2.4" + "yargs": "^15.3.1" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } } }, "core-js": { @@ -4575,6 +4764,13 @@ "requires": { "bn.js": "^4.1.0", "elliptic": "^6.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } } }, "create-hash": { @@ -4603,12 +4799,12 @@ } }, "create-react-context": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz", - "integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", + "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", "requires": { - "fbjs": "^0.8.0", - "gud": "^1.0.0" + "gud": "^1.0.0", + "warning": "^4.0.3" } }, "cross-fetch": { @@ -4773,43 +4969,13 @@ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, "css-selector-tokenizer": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", - "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz", + "integrity": "sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==", "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" - }, - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "requires": { - "jsesc": "~0.5.0" - } - } + "cssesc": "^3.0.0", + "fastparse": "^1.1.2", + "regexpu-core": "^4.6.0" } }, "css-tree": { @@ -4828,10 +4994,14 @@ } } }, - "css-unit-converter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz", - "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=" + "css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "requires": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } }, "css-what": { "version": "2.1.3", @@ -4845,9 +5015,9 @@ "dev": true }, "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, "cssnano": { "version": "4.1.10", @@ -4957,11 +5127,32 @@ "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==" }, "csso": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.2.tgz", - "integrity": "sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz", + "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==", "requires": { - "css-tree": "1.0.0-alpha.37" + "css-tree": "1.0.0-alpha.39" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz", + "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==", + "requires": { + "mdn-data": "2.0.6", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz", + "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } } }, "cssom": { @@ -5111,9 +5302,9 @@ } }, "defer-to-connect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.1.tgz", - "integrity": "sha512-J7thop4u3mRTkYRQ+Vpfwy2G5Ehoy82I14+14W4YMDLKdWloI9gSzRbV30s/NckQGVJtPkWNcW4oMAUigTdqiQ==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" }, "define-properties": { "version": "1.1.3", @@ -5274,9 +5465,9 @@ } }, "@types/node": { - "version": "7.10.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.9.tgz", - "integrity": "sha512-usSpgoUsRtO5xNV5YEPU8PPnHisFx8u0rokj1BPVn/hDF7zwUDzVLiuKZM38B7z8V2111Fj6kd4rGtQFUZpNOw==" + "version": "7.10.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.11.tgz", + "integrity": "sha512-uEqP1HlJFhsgD8DOBFdC72/5selvor0mzdQY97zlyo8Q6qPl849cFBWkNpgTXw3jIvb7iNyWsId51/A8HYKzbQ==" }, "debug": { "version": "2.6.9", @@ -5290,14 +5481,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "tmp": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "requires": { - "os-tmpdir": "~1.0.1" - } } } }, @@ -5315,6 +5498,13 @@ "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } } }, "dir-glob": { @@ -5394,9 +5584,9 @@ } }, "dom-walk": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", - "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" }, "domain-browser": { "version": "1.2.0", @@ -5484,9 +5674,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.340", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.340.tgz", - "integrity": "sha512-hRFBAglhcj5iVYH+o8QU0+XId1WGoc0VGowJB1cuJAt3exHGrivZvWeAO5BRgBZqwZtwxjm8a5MQeGoT/Su3ww==" + "version": "1.3.459", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.459.tgz", + "integrity": "sha512-aN3Z89qEYIwVjzGi9SrcTjjopRZ3STUA6xTufS0fxZy8xOO2iqVw8rYKdT32CHgOKHOYj5KGmz3n6xUKE4QJiQ==" }, "elliptic": { "version": "6.5.2", @@ -5500,6 +5690,13 @@ "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0", "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } } }, "emoji-regex": { @@ -5508,23 +5705,15 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "requires": { - "iconv-lite": "~0.4.13" - } - }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -5534,9 +5723,9 @@ } }, "engine.io": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz", - "integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.1.tgz", + "integrity": "sha512-8MfIfF1/IIfxuc2gv5K+XlFZczw/BpTvqBdl0E2fBLkYQp4miv4LuDTVtYt4yMyaIFLEr4vtaSgV4mjvll8Crw==", "requires": { "accepts": "~1.3.4", "base64id": "2.0.0", @@ -5562,11 +5751,11 @@ } }, "engine.io-client": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz", - "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.2.tgz", + "integrity": "sha512-AWjc1Xg06a6UPFOBAzJf48W1UR/qKYmv/ubgSCumo9GXgvL/xGIvo05dXoBL+2NTLMipDI7in8xK61C17L25xg==", "requires": { - "component-emitter": "1.2.1", + "component-emitter": "~1.3.0", "component-inherit": "0.0.3", "debug": "~4.1.0", "engine.io-parser": "~2.2.0", @@ -5579,11 +5768,6 @@ "yeast": "0.1.2" }, "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -5641,9 +5825,9 @@ "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" }, "envinfo": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.5.0.tgz", - "integrity": "sha512-jDgnJaF/Btomk+m3PZDTTCb5XIIIX3zYItnCRfF73zVgvinLoRomuhi75Y4su0PtQxWz4v66XnLLckyvyJTOIQ==" + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.5.1.tgz", + "integrity": "sha512-hQBkDf2iO4Nv0CNHpCuSBeaSrveU6nThVxFGTrq/eDlV716UQk09zChaJae4mZRsos1x4YLY2TaH3LHUae3ZmQ==" }, "eol": { "version": "0.8.1", @@ -5703,9 +5887,9 @@ } }, "es6-promisify": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.0.2.tgz", - "integrity": "sha512-eO6vFm0JvqGzjWIQA6QVKjxpmELfhWbDUWHm1rPfIbn55mhKPiAa5xpLmQWJrNa629ZIeQ8ZvMAi13kvrjK6Mg==" + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.1.1.tgz", + "integrity": "sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg==" }, "escape-html": { "version": "1.0.3", @@ -6289,14 +6473,14 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, "event-source-polyfill": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-1.0.12.tgz", - "integrity": "sha512-WjOTn0LIbaN08z/8gNt3GYAomAdm6cZ2lr/QdvhTTEipr5KR6lds2ziUH+p/Iob4Lk6NClKhwPOmn1NjQEcJCg==" + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-1.0.15.tgz", + "integrity": "sha512-IVmd8jWwX6ag5rXIdVCPBjBChiHBceLb1/7aKPIK7CUeJ5Br7alx029+ZpQlK4jW4Hk2qncy3ClJP97S8ltvmg==" }, "eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" }, "events": { "version": "3.1.0", @@ -6795,27 +6979,6 @@ "bser": "2.1.1" } }, - "fbjs": { - "version": "0.8.17", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", - "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", - "requires": { - "core-js": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.18" - }, - "dependencies": { - "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" - } - } - }, "figgy-pudding": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", @@ -6844,6 +7007,17 @@ "requires": { "loader-utils": "^1.0.2", "schema-utils": "^0.4.5" + }, + "dependencies": { + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + } } }, "file-uri-to-path": { @@ -7083,1032 +7257,198 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", - "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "optional": true, "requires": { "bindings": "^1.5.0", - "nan": "^2.12.1", - "node-pre-gyp": "*" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "optional": true - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "optional": true - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "optional": true - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "optional": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "optional": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "optional": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "optional": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "optional": true, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true - }, - "needle": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", - "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", - "optional": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz", - "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "optional": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "optional": true - }, - "npm-packlist": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.7.tgz", - "integrity": "sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ==", - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "optional": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "optional": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "optional": true - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "optional": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "optional": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "optional": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "optional": true - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "optional": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "optional": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" - }, - "gatsby": { - "version": "2.19.7", - "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-2.19.7.tgz", - "integrity": "sha512-pdCXdzH7Ht0KcpGJPV//WIWgZ10vxO5Gqs4N3NvfAAAKnhZotJ8W5pbSfbw+7ec5zxNw57SWPE5hV6Unm9rIYA==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/core": "^7.7.5", - "@babel/parser": "^7.7.5", - "@babel/polyfill": "^7.7.0", - "@babel/runtime": "^7.7.6", - "@babel/traverse": "^7.7.4", - "@hapi/joi": "^15.1.1", - "@mikaelkristiansson/domready": "^1.0.10", - "@pieh/friendly-errors-webpack-plugin": "1.7.0-chalk-2", - "@reach/router": "^1.2.1", - "@typescript-eslint/eslint-plugin": "^2.11.0", - "@typescript-eslint/parser": "^2.11.0", - "address": "1.1.2", - "autoprefixer": "^9.7.3", - "axios": "^0.19.0", - "babel-core": "7.0.0-bridge.0", - "babel-eslint": "^10.0.3", - "babel-loader": "^8.0.6", - "babel-plugin-add-module-exports": "^0.3.3", - "babel-plugin-dynamic-import-node": "^2.3.0", - "babel-plugin-remove-graphql-queries": "^2.7.22", - "babel-preset-gatsby": "^0.2.27", - "better-opn": "1.0.0", - "better-queue": "^3.8.10", - "bluebird": "^3.7.2", - "browserslist": "3.2.8", - "cache-manager": "^2.10.1", - "cache-manager-fs-hash": "^0.0.7", - "chalk": "^2.4.2", - "chokidar": "3.3.0", - "common-tags": "^1.8.0", - "compression": "^1.7.4", - "convert-hrtime": "^3.0.0", - "copyfiles": "^2.1.1", - "core-js": "^2.6.11", - "cors": "^2.8.5", - "css-loader": "^1.0.1", - "debug": "^3.2.6", - "del": "^5.1.0", - "detect-port": "^1.3.0", - "devcert-san": "^0.3.3", - "dotenv": "^8.2.0", - "eslint": "^6.7.2", - "eslint-config-react-app": "^5.1.0", - "eslint-loader": "^2.2.1", - "eslint-plugin-flowtype": "^3.13.0", - "eslint-plugin-graphql": "^3.1.0", - "eslint-plugin-import": "^2.19.1", - "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-react": "^7.17.0", - "eslint-plugin-react-hooks": "^1.7.0", - "event-source-polyfill": "^1.0.11", - "express": "^4.17.1", - "express-graphql": "^0.9.0", - "fast-levenshtein": "^2.0.6", - "file-loader": "^1.1.11", - "flat": "^4.1.0", - "fs-exists-cached": "1.0.0", - "fs-extra": "^8.1.0", - "gatsby-cli": "^2.8.27", - "gatsby-core-utils": "^1.0.26", - "gatsby-graphiql-explorer": "^0.2.32", - "gatsby-link": "^2.2.28", - "gatsby-plugin-page-creator": "^2.1.38", - "gatsby-react-router-scroll": "^2.1.20", - "gatsby-telemetry": "^1.1.47", - "glob": "^7.1.6", - "got": "8.3.2", - "graphql": "^14.5.8", - "graphql-compose": "^6.3.7", - "graphql-playground-middleware-express": "^1.7.12", - "hasha": "^5.1.0", - "invariant": "^2.2.4", - "is-relative": "^1.0.0", - "is-relative-url": "^3.0.0", - "is-wsl": "^2.1.1", - "jest-worker": "^24.9.0", - "json-loader": "^0.5.7", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "lokijs": "^1.5.8", - "md5": "^2.2.1", - "md5-file": "^3.2.3", - "micromatch": "^3.1.10", - "mime": "^2.4.4", - "mini-css-extract-plugin": "^0.8.0", - "mitt": "^1.2.0", - "mkdirp": "^0.5.1", - "moment": "^2.24.0", - "name-all-modules-plugin": "^1.0.1", - "normalize-path": "^2.1.1", - "null-loader": "^0.1.1", - "opentracing": "^0.14.4", - "optimize-css-assets-webpack-plugin": "^5.0.3", - "p-defer": "^3.0.0", - "parseurl": "^1.3.3", - "physical-cpu-count": "^2.0.0", - "pnp-webpack-plugin": "^1.5.0", - "postcss-flexbugs-fixes": "^3.3.1", - "postcss-loader": "^2.1.6", - "prompts": "^2.3.0", - "prop-types": "^15.7.2", - "raw-loader": "^0.5.1", - "react-dev-utils": "^4.2.3", - "react-error-overlay": "^3.0.0", - "react-hot-loader": "^4.12.18", - "redux": "^4.0.4", - "redux-thunk": "^2.3.0", - "semver": "^5.7.1", - "shallow-compare": "^1.2.2", - "sift": "^5.1.0", - "signal-exit": "^3.0.2", - "slugify": "^1.3.6", - "socket.io": "^2.3.0", - "stack-trace": "^0.0.10", - "string-similarity": "^1.2.2", - "style-loader": "^0.23.1", - "terser-webpack-plugin": "^1.4.2", - "true-case-path": "^2.2.1", - "type-of": "^2.0.1", - "url-loader": "^1.1.2", - "util.promisify": "^1.0.0", - "uuid": "^3.3.3", - "v8-compile-cache": "^1.1.2", - "webpack": "~4.41.2", - "webpack-dev-middleware": "^3.7.2", - "webpack-dev-server": "^3.9.0", - "webpack-hot-middleware": "^2.25.0", - "webpack-merge": "^4.2.2", - "webpack-stats-plugin": "^0.3.0", - "xstate": "^4.7.2", - "yaml-loader": "^0.5.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "configstore": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.0.tgz", - "integrity": "sha512-eE/hvMs7qw7DlcB5JPRnthmrITuHMmACUJAp89v6PT6iOqzoLS7HRWhBtuHMlhNHo2AhUSA/3Dh1bKNJHcublQ==", - "requires": { - "dot-prop": "^5.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" - }, - "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "requires": { - "is-obj": "^2.0.0" - } - }, - "gatsby-cli": { - "version": "2.8.27", - "resolved": "https://registry.npmjs.org/gatsby-cli/-/gatsby-cli-2.8.27.tgz", - "integrity": "sha512-bwLk3zwa2SNVqI6TWzYFTzkQzqPPBy3OdTqffROlxpm+2BqkKxNWP4NTQ1Ea6Hq0IuRI4iM4Mm7OxKf0knbbyQ==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/runtime": "^7.7.6", - "@hapi/joi": "^15.1.1", - "better-opn": "^1.0.0", - "bluebird": "^3.7.2", - "chalk": "^2.4.2", - "clipboardy": "^2.1.0", - "common-tags": "^1.8.0", - "configstore": "^5.0.0", - "convert-hrtime": "^3.0.0", - "core-js": "^2.6.11", - "envinfo": "^7.5.0", - "execa": "^3.4.0", - "fs-exists-cached": "^1.0.0", - "fs-extra": "^8.1.0", - "gatsby-core-utils": "^1.0.26", - "gatsby-telemetry": "^1.1.47", - "hosted-git-info": "^3.0.2", - "ink": "^2.6.0", - "ink-spinner": "^3.0.1", - "is-valid-path": "^0.1.1", - "lodash": "^4.17.15", - "meant": "^1.0.1", - "node-fetch": "^2.6.0", - "object.entries": "^1.1.0", - "opentracing": "^0.14.4", - "pretty-error": "^2.1.1", - "progress": "^2.0.3", - "prompts": "^2.3.0", - "react": "^16.12.0", - "redux": "^4.0.4", - "resolve-cwd": "^2.0.0", - "semver": "^6.3.0", - "signal-exit": "^3.0.2", - "source-map": "0.7.3", - "stack-trace": "^0.0.10", - "strip-ansi": "^5.2.0", - "update-notifier": "^3.0.1", - "uuid": "3.3.3", - "yargs": "^12.0.5", - "yurnalist": "^1.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" - } - } - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" - }, - "hosted-git-info": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.2.tgz", - "integrity": "sha512-ezZMWtHXm7Eb7Rq4Mwnx2vs79WUx2QmRg3+ZqeGroKzfDO+EprOcgRPYghsOP9JuYBfK18VojmRTGCg8Ma+ktw==", - "requires": { - "lru-cache": "^5.1.1" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", - "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "write-file-atomic": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", - "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "nan": "^2.12.1" } }, - "gatsby-cli": { - "version": "2.10.2", - "resolved": "https://registry.npmjs.org/gatsby-cli/-/gatsby-cli-2.10.2.tgz", - "integrity": "sha512-TAwdMptYEKIWC93MHn3Vr+UZMOCxiAp3qN17+depCm2lmYq9O4DgtQi8FPHIZfivIJjAhQ1TOYXl9890YclfLg==", - "dev": true, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "gatsby": { + "version": "2.18.25", + "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-2.18.25.tgz", + "integrity": "sha512-mXn1OY93On6D8YDYv4qpC4S8e83EzeSnzSgISgSlyPEKilXQYdAYNJn+QdfC74dLsm7lsaDeLKcb8PIjegaiQw==", "requires": { "@babel/code-frame": "^7.5.5", + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/polyfill": "^7.7.0", "@babel/runtime": "^7.7.6", + "@babel/traverse": "^7.7.4", + "@hapi/joi": "^15.1.1", + "@mikaelkristiansson/domready": "^1.0.10", + "@pieh/friendly-errors-webpack-plugin": "1.7.0-chalk-2", + "@reach/router": "^1.2.1", + "@typescript-eslint/eslint-plugin": "^2.11.0", + "@typescript-eslint/parser": "^2.11.0", + "address": "1.1.2", + "autoprefixer": "^9.7.3", + "axios": "^0.19.0", + "babel-core": "7.0.0-bridge.0", + "babel-eslint": "^10.0.3", + "babel-loader": "^8.0.6", + "babel-plugin-add-module-exports": "^0.3.3", + "babel-plugin-dynamic-import-node": "^2.3.0", + "babel-plugin-remove-graphql-queries": "^2.7.22", + "babel-preset-gatsby": "^0.2.27", + "better-opn": "1.0.0", + "better-queue": "^3.8.10", + "bluebird": "^3.7.2", + "browserslist": "3.2.8", + "cache-manager": "^2.10.1", + "cache-manager-fs-hash": "^0.0.7", + "chalk": "^2.4.2", + "chokidar": "3.3.0", + "common-tags": "^1.8.0", + "compression": "^1.7.4", + "convert-hrtime": "^3.0.0", + "copyfiles": "^2.1.1", + "core-js": "^2.6.11", + "cors": "^2.8.5", + "css-loader": "^1.0.1", + "debug": "^3.2.6", + "del": "^5.1.0", + "detect-port": "^1.3.0", + "devcert-san": "^0.3.3", + "dotenv": "^8.2.0", + "eslint": "^6.7.2", + "eslint-config-react-app": "^5.1.0", + "eslint-loader": "^2.2.1", + "eslint-plugin-flowtype": "^3.13.0", + "eslint-plugin-graphql": "^3.1.0", + "eslint-plugin-import": "^2.19.1", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-react": "^7.17.0", + "eslint-plugin-react-hooks": "^1.7.0", + "event-source-polyfill": "^1.0.11", + "express": "^4.17.1", + "express-graphql": "^0.9.0", + "fast-levenshtein": "^2.0.6", + "file-loader": "^1.1.11", + "flat": "^4.1.0", + "fs-exists-cached": "1.0.0", + "fs-extra": "^8.1.0", + "gatsby-cli": "^2.8.27", + "gatsby-core-utils": "^1.0.26", + "gatsby-graphiql-explorer": "^0.2.32", + "gatsby-link": "^2.2.28", + "gatsby-plugin-page-creator": "^2.1.38", + "gatsby-react-router-scroll": "^2.1.20", + "gatsby-telemetry": "^1.1.47", + "glob": "^7.1.6", + "got": "8.3.2", + "graphql": "^14.5.8", + "graphql-compose": "^6.3.7", + "graphql-playground-middleware-express": "^1.7.12", + "invariant": "^2.2.4", + "is-relative": "^1.0.0", + "is-relative-url": "^3.0.0", + "is-wsl": "^2.1.1", + "jest-worker": "^24.9.0", + "json-loader": "^0.5.7", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "lokijs": "^1.5.8", + "md5": "^2.2.1", + "md5-file": "^3.2.3", + "micromatch": "^3.1.10", + "mime": "^2.4.4", + "mini-css-extract-plugin": "^0.8.0", + "mitt": "^1.2.0", + "mkdirp": "^0.5.1", + "moment": "^2.24.0", + "name-all-modules-plugin": "^1.0.1", + "normalize-path": "^2.1.1", + "null-loader": "^0.1.1", + "opentracing": "^0.14.4", + "optimize-css-assets-webpack-plugin": "^5.0.3", + "parseurl": "^1.3.3", + "physical-cpu-count": "^2.0.0", + "pnp-webpack-plugin": "^1.5.0", + "postcss-flexbugs-fixes": "^3.3.1", + "postcss-loader": "^2.1.6", + "prompts": "^2.3.0", + "prop-types": "^15.7.2", + "raw-loader": "^0.5.1", + "react-dev-utils": "^4.2.3", + "react-error-overlay": "^3.0.0", + "react-hot-loader": "^4.12.18", + "redux": "^4.0.4", + "redux-thunk": "^2.3.0", + "semver": "^5.7.1", + "shallow-compare": "^1.2.2", + "sift": "^5.1.0", + "signal-exit": "^3.0.2", + "slugify": "^1.3.6", + "socket.io": "^2.3.0", + "stack-trace": "^0.0.10", + "string-similarity": "^1.2.2", + "style-loader": "^0.23.1", + "terser-webpack-plugin": "^1.4.2", + "true-case-path": "^2.2.1", + "type-of": "^2.0.1", + "url-loader": "^1.1.2", + "util.promisify": "^1.0.0", + "uuid": "^3.3.3", + "v8-compile-cache": "^1.1.2", + "webpack": "~4.41.2", + "webpack-dev-middleware": "^3.7.2", + "webpack-dev-server": "^3.9.0", + "webpack-hot-middleware": "^2.25.0", + "webpack-merge": "^4.2.2", + "webpack-stats-plugin": "^0.3.0", + "xstate": "^4.7.2", + "yaml-loader": "^0.5.0" + } + }, + "gatsby-cli": { + "version": "2.10.13", + "resolved": "https://registry.npmjs.org/gatsby-cli/-/gatsby-cli-2.10.13.tgz", + "integrity": "sha512-sha55CAV5LULuYkESvQvVSC91qnkx8uM8OkTup1zwIvS7IL/WjUJKXekHEMmqY01A9EzbR5bRbs+uGydxN56CA==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/runtime": "^7.8.7", "@hapi/joi": "^15.1.1", "better-opn": "^1.0.0", "bluebird": "^3.7.2", "chalk": "^2.4.2", - "clipboardy": "^2.1.0", + "clipboardy": "^2.2.0", "common-tags": "^1.8.0", - "configstore": "^5.0.0", + "configstore": "^5.0.1", "convert-hrtime": "^3.0.0", "core-js": "^2.6.11", "envinfo": "^7.5.0", "execa": "^3.4.0", "fs-exists-cached": "^1.0.0", "fs-extra": "^8.1.0", - "gatsby-core-utils": "^1.0.30", - "gatsby-telemetry": "^1.1.51", - "hosted-git-info": "^3.0.2", - "ink": "^2.6.0", + "gatsby-core-utils": "^1.0.34", + "gatsby-telemetry": "^1.1.56", + "hosted-git-info": "^3.0.4", + "ink": "^2.7.1", "ink-spinner": "^3.0.1", "is-valid-path": "^0.1.1", "lodash": "^4.17.15", "meant": "^1.0.1", "node-fetch": "^2.6.0", - "object.entries": "^1.1.0", + "object.entries": "^1.1.1", "opentracing": "^0.14.4", "pretty-error": "^2.1.1", "progress": "^2.0.3", - "prompts": "^2.3.0", + "prompts": "^2.3.1", "react": "^16.8.0", - "redux": "^4.0.4", + "redux": "^4.0.5", "resolve-cwd": "^2.0.0", "semver": "^6.3.0", "signal-exit": "^3.0.2", @@ -8116,22 +7456,28 @@ "stack-trace": "^0.0.10", "strip-ansi": "^5.2.0", "update-notifier": "^3.0.1", - "uuid": "3.3.3", + "uuid": "3.4.0", "yargs": "^12.0.5", - "yurnalist": "^1.1.1" + "yurnalist": "^1.1.2" }, "dependencies": { + "@babel/runtime": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", + "integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, "requires": { "string-width": "^2.1.1", "strip-ansi": "^4.0.0", @@ -8141,14 +7487,12 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, "requires": { "ansi-regex": "^3.0.0" } @@ -8159,7 +7503,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, "requires": { "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", @@ -8172,65 +7515,38 @@ "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" }, "dot-prop": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "dev": true, "requires": { "is-obj": "^2.0.0" } }, "gatsby-core-utils": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-1.0.30.tgz", - "integrity": "sha512-+WYFxf9dT4u67vecXdROe9iF2jzPKoTVbWI3FYLCcNANY2Y/vEec11vhG5FnBHdLpgmD3cnLPdhCAileKWBldA==", - "dev": true, + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-1.3.4.tgz", + "integrity": "sha512-LSNwM14pKQP93Qvsa13pneHKxoKX6dbQ+Z7PTKm6aWR4JY+jiHUCN8RyFH1GsL6iB5AHeMUslJZx/X0eUQJrsQ==", "requires": { "ci-info": "2.0.0", - "configstore": "^5.0.0", - "node-object-hash": "^2.0.0" - } - }, - "gatsby-telemetry": { - "version": "1.1.51", - "resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-1.1.51.tgz", - "integrity": "sha512-34SvdYWn/nJTYMiof9rAgvlcYqpzDbadfF1egMR3Kv+7dTBN5UVkeGfjUixsaD+sg6PVA+06CC5TMz8WQXCBWA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/runtime": "^7.7.6", - "bluebird": "^3.7.2", - "boxen": "^4.2.0", - "configstore": "^5.0.0", - "envinfo": "^7.5.0", + "configstore": "^5.0.1", "fs-extra": "^8.1.0", - "gatsby-core-utils": "^1.0.30", - "git-up": "4.0.1", - "is-docker": "2.0.0", - "lodash": "^4.17.15", - "node-fetch": "2.6.0", - "resolve-cwd": "^2.0.0", - "source-map": "^0.7.3", - "stack-trace": "^0.0.10", - "stack-utils": "1.0.2", - "uuid": "3.3.3" + "node-object-hash": "^2.0.0", + "proper-lockfile": "^4.1.1", + "xdg-basedir": "^4.0.0" } }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, "hosted-git-info": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz", "integrity": "sha512-4oT62d2jwSDBbLLFLZE+1vPuQ1h8p9wjrJ8Mqx5TjsyWmBMV5B13eJqn8pvluqubLf3cJPTfiYCIwNwDNmzScQ==", - "dev": true, "requires": { "lru-cache": "^5.1.1" } @@ -8239,7 +7555,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8247,56 +7562,57 @@ "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "requires": { "yallist": "^3.0.2" } }, "make-dir": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", - "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", - "dev": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "requires": { "semver": "^6.0.0" } }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", - "dev": true + "prompts": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", + "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.4" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" }, "require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -8305,22 +7621,14 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, "requires": { "crypto-random-string": "^2.0.0" } }, - "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", - "dev": true - }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" @@ -8329,14 +7637,12 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8347,7 +7653,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8358,7 +7663,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, "requires": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -8369,20 +7673,17 @@ "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "yargs": { "version": "12.0.5", "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, "requires": { "cliui": "^4.0.0", "decamelize": "^1.2.0", @@ -8402,7 +7703,6 @@ "version": "11.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -8420,21 +7720,51 @@ } }, "gatsby-graphiql-explorer": { - "version": "0.2.32", - "resolved": "https://registry.npmjs.org/gatsby-graphiql-explorer/-/gatsby-graphiql-explorer-0.2.32.tgz", - "integrity": "sha512-0Fzx5JOevUBtD8s94q3Pfjp0LPSVHWNijGXBfRRdcpzKPJZuvYAzVkRz5aQgx1FPavSJE8q9uC+Uo4VzyyPpJg==", + "version": "0.2.37", + "resolved": "https://registry.npmjs.org/gatsby-graphiql-explorer/-/gatsby-graphiql-explorer-0.2.37.tgz", + "integrity": "sha512-dWfNA/CDjKO86DZLgxhYFSmK7DTCxwGvKm0HeMBYxcSyLP/WFAOoJjV2DCE2gMge28Sqmsz8ueOMZXM2YH8rIA==", "requires": { - "@babel/runtime": "^7.7.6" + "@babel/runtime": "^7.8.7" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", + "integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + } } }, "gatsby-link": { - "version": "2.2.28", - "resolved": "https://registry.npmjs.org/gatsby-link/-/gatsby-link-2.2.28.tgz", - "integrity": "sha512-G1ivEChE2eNxrPoKXR3Z27bZUWBNcL+SybJ+V0+xnR2y/w/hLe7DTOCt/UTCKgtvJ0jvQodwK7r1+HroXmsnVw==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/gatsby-link/-/gatsby-link-2.4.5.tgz", + "integrity": "sha512-h684vfaHkvkU4m18ExQWKA/iU/nDQ4SpQf5wsY7aWB7iHKS1Orxd2Ol9d9ndv1qi4WyUncgl66uqbDC29BegQg==", "requires": { - "@babel/runtime": "^7.7.6", - "@types/reach__router": "^1.2.6", + "@babel/runtime": "^7.10.2", + "@types/reach__router": "^1.3.3", "prop-types": "^15.7.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", + "integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + } } }, "gatsby-page-utils": { @@ -8695,13 +8025,36 @@ "integrity": "sha512-/yA6rFjfjiFb8D6nCjfFrrGqYQMkOt4J3u2o6s7VYEF/zpA5dw2C9ENJ5fDKkJSCbbwLiEIGVMMee3vMEip2zA==" }, "gatsby-react-router-scroll": { - "version": "2.1.20", - "resolved": "https://registry.npmjs.org/gatsby-react-router-scroll/-/gatsby-react-router-scroll-2.1.20.tgz", - "integrity": "sha512-o4LvoV+XL8zLLFsX1rN2Und4seu2jppEcj6yb9g/f5itsPzNXMdRWW8r2fqovGRxtDJ/J6Wwx+1y3pOokMSq/A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/gatsby-react-router-scroll/-/gatsby-react-router-scroll-2.3.1.tgz", + "integrity": "sha512-l1yTR43T05k+7rhiE2O/S4kNsjacveyLzai7BGGo1N7qIvd/E3p2bWNEtr4BUbzel76aZJRQ3BXENbHyDDAuJw==", "requires": { - "@babel/runtime": "^7.7.6", - "scroll-behavior": "^0.9.10", + "@babel/runtime": "^7.9.6", + "scroll-behavior": "^0.9.12", "warning": "^3.0.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", + "integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + }, + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } } }, "gatsby-source-filesystem": { @@ -8786,18 +8139,18 @@ } }, "gatsby-telemetry": { - "version": "1.1.47", - "resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-1.1.47.tgz", - "integrity": "sha512-vj3zGaB6Of3ExYk/VGF91qh6YcB/ofT9yYYbefO741rlK3iusv8Fzg13R8yPyRBHYOtKhgvXNbUUgH8sWHUq4Q==", + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-1.3.10.tgz", + "integrity": "sha512-Lp2DJhuPh5f5swYfIMcSYfXMi1bdoiO8Z2b3ZW2m+mDZk77dwEQg3dZPXlLyHGxboiO0D8Q7Jz6Fin9mO0T3CA==", "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/runtime": "^7.7.6", + "@babel/code-frame": "^7.10.1", + "@babel/runtime": "^7.10.2", "bluebird": "^3.7.2", "boxen": "^4.2.0", - "configstore": "^5.0.0", - "envinfo": "^7.5.0", + "configstore": "^5.0.1", + "envinfo": "^7.5.1", "fs-extra": "^8.1.0", - "gatsby-core-utils": "^1.0.26", + "gatsby-core-utils": "^1.3.4", "git-up": "4.0.1", "is-docker": "2.0.0", "lodash": "^4.17.15", @@ -8806,15 +8159,41 @@ "source-map": "^0.7.3", "stack-trace": "^0.0.10", "stack-utils": "1.0.2", - "uuid": "3.3.3" + "uuid": "3.4.0" }, "dependencies": { + "@babel/code-frame": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", + "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", + "requires": { + "@babel/highlight": "^7.10.1" + } + }, + "@babel/highlight": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", + "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.1", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/runtime": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", + "integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "configstore": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.0.tgz", - "integrity": "sha512-eE/hvMs7qw7DlcB5JPRnthmrITuHMmACUJAp89v6PT6iOqzoLS7HRWhBtuHMlhNHo2AhUSA/3Dh1bKNJHcublQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", "requires": { - "dot-prop": "^5.1.0", + "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", "make-dir": "^3.0.0", "unique-string": "^2.0.0", @@ -8835,15 +8214,28 @@ "is-obj": "^2.0.0" } }, + "gatsby-core-utils": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-1.3.4.tgz", + "integrity": "sha512-LSNwM14pKQP93Qvsa13pneHKxoKX6dbQ+Z7PTKm6aWR4JY+jiHUCN8RyFH1GsL6iB5AHeMUslJZx/X0eUQJrsQ==", + "requires": { + "ci-info": "2.0.0", + "configstore": "^5.0.1", + "fs-extra": "^8.1.0", + "node-object-hash": "^2.0.0", + "proper-lockfile": "^4.1.1", + "xdg-basedir": "^4.0.0" + } + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" }, "make-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", - "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "requires": { "semver": "^6.0.0" } @@ -8853,6 +8245,11 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -8871,15 +8268,10 @@ "crypto-random-string": "^2.0.0" } }, - "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" - }, "write-file-atomic": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", - "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "requires": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -9157,9 +8549,9 @@ } }, "graphql-config": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-2.2.1.tgz", - "integrity": "sha512-U8+1IAhw9m6WkZRRcyj8ZarK96R6lQBQ0an4lp76Ps9FyhOXENC5YQOxOFGm5CxPrX2rD0g3Je4zG5xdNJjwzQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-2.2.2.tgz", + "integrity": "sha512-mtv1ejPyyR2mJUUZNhljggU+B/Xl8tJJWf+h145hB+1Y48acSghFalhNtXfPBcYl2tJzpb+lGxfj3O7OjaiMgw==", "requires": { "graphql-import": "^0.7.1", "graphql-request": "^1.5.0", @@ -9178,16 +8570,16 @@ } }, "graphql-playground-html": { - "version": "1.6.12", - "resolved": "https://registry.npmjs.org/graphql-playground-html/-/graphql-playground-html-1.6.12.tgz", - "integrity": "sha512-yOYFwwSMBL0MwufeL8bkrNDgRE7eF/kTHiwrqn9FiR9KLcNIl1xw9l9a+6yIRZM56JReQOHpbQFXTZn1IuSKRg==" + "version": "1.6.20", + "resolved": "https://registry.npmjs.org/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", + "integrity": "sha512-RkC18un0a1YEm0PoTMGgFQh7kIA6mtp3dUun+6coWtuMLczoNNij6V0DPHEj5kWi8u0qIrSKgSx5kh4pxcCX6g==" }, "graphql-playground-middleware-express": { - "version": "1.7.12", - "resolved": "https://registry.npmjs.org/graphql-playground-middleware-express/-/graphql-playground-middleware-express-1.7.12.tgz", - "integrity": "sha512-17szgonnVSxWVrgblLRHHLjWnMUONfkULIwSunaMvYx8k5oG3yL86cyGCbHuDFUFkyr2swLhdfYl4mDfDXuvOA==", + "version": "1.7.15", + "resolved": "https://registry.npmjs.org/graphql-playground-middleware-express/-/graphql-playground-middleware-express-1.7.15.tgz", + "integrity": "sha512-Q7bjD1SMT5fiXMgUqstNzkYk9+csbuu5K7uOga9tJlA8x9gOVsSmmIfLi0tjPOrPd4m8icPnKncR73oNA22d5g==", "requires": { - "graphql-playground-html": "1.6.12" + "graphql-playground-html": "^1.6.19" } }, "graphql-request": { @@ -9224,9 +8616,9 @@ } }, "handle-thing": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", - "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" }, "har-schema": { "version": "2.0.0", @@ -9351,12 +8743,30 @@ "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" }, "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "hash.js": { @@ -9368,22 +8778,6 @@ "minimalistic-assert": "^1.0.1" } }, - "hasha": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.1.0.tgz", - "integrity": "sha512-OFPDWmzPN1l7atOV1TgBVmNtBxaIysToK6Ve9DK+vT6pYuklw/nPNT+HJbZi0KDcI6vWB+9tgvZ5YD7fA3CXcA==", - "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "dependencies": { - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - } - } - }, "hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", @@ -9456,9 +8850,9 @@ } }, "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==" }, "html-escaper": { "version": "2.0.0", @@ -9497,9 +8891,9 @@ } }, "http-cache-semantics": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", - "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, "http-deceiver": { "version": "1.2.7", @@ -9526,14 +8920,14 @@ } }, "http-parser-js": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", - "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=" + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", + "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==" }, "http-proxy": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "requires": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -9735,17 +9129,17 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "ink": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/ink/-/ink-2.6.0.tgz", - "integrity": "sha512-nD/wlSuB6WnFsFB0nUcOJdy28YvvDer3eo+gezjvZqojGA4Rx5sQpacvN//Aai83DRgwrRTyKBl5aciOcfP3zQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/ink/-/ink-2.7.1.tgz", + "integrity": "sha512-s7lJuQDJEdjqtaIWhp3KYHl6WV3J04U9zoQ6wVc+Xoa06XM27SXUY57qC5DO46xkF0CfgXMKkKNcgvSu/SAEpA==", "optional": true, "requires": { "ansi-escapes": "^4.2.1", "arrify": "^2.0.1", - "auto-bind": "^3.0.0", + "auto-bind": "^4.0.0", "chalk": "^3.0.0", "cli-cursor": "^3.1.0", - "cli-truncate": "^2.0.0", + "cli-truncate": "^2.1.0", "is-ci": "^2.0.0", "lodash.throttle": "^4.1.1", "log-update": "^3.0.0", @@ -10006,13 +9400,6 @@ "requires": { "from2": "^2.1.1", "p-is-promise": "^1.1.0" - }, - "dependencies": { - "p-is-promise": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" - } } }, "invariant": { @@ -10202,6 +9589,11 @@ "is-extglob": "^2.1.1" } }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=" + }, "is-installed-globally": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", @@ -10452,15 +9844,6 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - } - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -13773,6 +13156,83 @@ "verror": "1.10.0" } }, + "jss": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz", + "integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "csstype": "^2.6.5", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-camel-case": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.1.1.tgz", + "integrity": "sha512-MDIaw8FeD5uFz1seQBKz4pnvDLnj5vIKV5hXSVdMaAVq13xR6SVTVWkIV/keyTs5txxTvzGJ9hXoxgd1WTUlBw==", + "requires": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.1.1" + } + }, + "jss-plugin-default-unit": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.1.1.tgz", + "integrity": "sha512-UkeVCA/b3QEA4k0nIKS4uWXDCNmV73WLHdh2oDGZZc3GsQtlOCuiH3EkB/qI60v2MiCq356/SYWsDXt21yjwdg==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.1.1" + } + }, + "jss-plugin-global": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.1.1.tgz", + "integrity": "sha512-VBG3wRyi3Z8S4kMhm8rZV6caYBegsk+QnQZSVmrWw6GVOT/Z4FA7eyMu5SdkorDlG/HVpHh91oFN56O4R9m2VA==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.1.1" + } + }, + "jss-plugin-nested": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.1.1.tgz", + "integrity": "sha512-ozEu7ZBSVrMYxSDplPX3H82XHNQk2DQEJ9TEyo7OVTPJ1hEieqjDFiOQOxXEj9z3PMqkylnUbvWIZRDKCFYw5Q==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.1.1", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.1.1.tgz", + "integrity": "sha512-g/joK3eTDZB4pkqpZB38257yD4LXB0X15jxtZAGbUzcKAVUHPl9Jb47Y7lYmiGsShiV4YmQRqG1p2DHMYoK91g==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.1.1" + } + }, + "jss-plugin-rule-value-function": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.1.1.tgz", + "integrity": "sha512-ClV1lvJ3laU9la1CUzaDugEcwnpjPTuJ0yGy2YtcU+gG/w9HMInD5vEv7xKAz53Bk4WiJm5uLOElSEshHyhKNw==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.1.1" + } + }, + "jss-plugin-vendor-prefixer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.1.1.tgz", + "integrity": "sha512-09MZpQ6onQrhaVSF6GHC4iYifQ7+4YC/tAP6D4ZWeZotvCMq1mHLqNKRIaqQ2lkgANjlEot2JnVi1ktu4+L4pw==", + "requires": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.7", + "jss": "10.1.1" + } + }, "jsx-ast-utils": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz", @@ -13940,12 +13400,12 @@ "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" }, "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "requires": { "big.js": "^5.2.2", - "emojis-list": "^2.0.0", + "emojis-list": "^3.0.0", "json5": "^1.0.1" }, "dependencies": { @@ -13991,6 +13451,11 @@ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -14081,9 +13546,9 @@ "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, "log-update": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-3.3.0.tgz", - "integrity": "sha512-YSKm5n+YjZoGZT5lfmOqasVH1fIH9xQA9A81Y48nZ99PxAP62vdCCtua+Gcu6oTn0nqtZd/LwRV+Vflo53ZDWA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-3.4.0.tgz", + "integrity": "sha512-ILKe88NeMt4gmDvk/eb615U/IVn7K9KWGkoYbdatQ69Z65nj1ZzjM6fHXfcs0Uge+e+EGnMW7DY4T9yko8vWFg==", "optional": true, "requires": { "ansi-escapes": "^3.2.0", @@ -14134,9 +13599,9 @@ } }, "loglevel": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz", - "integrity": "sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==" + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz", + "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==" }, "lokijs": { "version": "1.5.8", @@ -14212,13 +13677,6 @@ "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "requires": { "p-defer": "^1.0.0" - }, - "dependencies": { - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" - } } }, "map-cache": { @@ -14285,6 +13743,13 @@ "map-age-cleaner": "^0.1.1", "mimic-fn": "^2.0.0", "p-is-promise": "^2.0.0" + }, + "dependencies": { + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" + } } }, "memory-fs": { @@ -14348,6 +13813,13 @@ "requires": { "bn.js": "^4.0.0", "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } } }, "mime": { @@ -14650,13 +14122,9 @@ "integrity": "sha1-n7CwmbzSoCGUDmA8ZCVNwAPZp6g=" }, "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" }, "node-forge": { "version": "0.9.0", @@ -15040,11 +14508,6 @@ } } }, - "opencollective-postinstall": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", - "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==" - }, "opentracing": { "version": "0.14.4", "resolved": "https://registry.npmjs.org/opentracing/-/opentracing-0.14.4.tgz", @@ -15137,9 +14600,9 @@ "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" }, "p-defer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-3.0.0.tgz", - "integrity": "sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" }, "p-each-series": { "version": "2.1.0", @@ -15153,9 +14616,9 @@ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" }, "p-limit": { "version": "2.2.2", @@ -15390,9 +14853,9 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-wHMFZ6HTLGlB9f/WsQBs5OwMQJoLXYuJUzbA+j+hRBf7+Y8KcXpatzIviIcTy1OAyhWQp08nyiPO8Dnv0z4Sww==", "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -15452,6 +14915,54 @@ "find-up": "^3.0.0" } }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "requires": { + "find-up": "^2.1.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + } + } + }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -15459,11 +14970,11 @@ "dev": true }, "pnp-webpack-plugin": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.5.0.tgz", - "integrity": "sha512-jd9olUr9D7do+RN8Wspzhpxhgp1n6Vd0NtQ4SFkmIACZoEL1nkyAdW9Ygrinjec0vgDcWjscFQQ1gDW8rsfKTg==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", + "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==", "requires": { - "ts-pnp": "^1.1.2" + "ts-pnp": "^1.1.6" } }, "popper.js": { @@ -15472,9 +14983,9 @@ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" }, "portfinder": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", - "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz", + "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==", "requires": { "async": "^2.6.2", "debug": "^3.1.1", @@ -15497,9 +15008,9 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { - "version": "7.0.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.26.tgz", - "integrity": "sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA==", + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", "requires": { "chalk": "^2.4.2", "source-map": "^0.6.1", @@ -15522,21 +15033,13 @@ } }, "postcss-calc": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz", - "integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz", + "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==", "requires": { - "css-unit-converter": "^1.1.1", - "postcss": "^7.0.5", - "postcss-selector-parser": "^5.0.0-rc.4", - "postcss-value-parser": "^3.3.1" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" } }, "postcss-colormin": { @@ -15552,42 +15055,30 @@ }, "dependencies": { "browserslist": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz", - "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", "requires": { - "caniuse-lite": "^1.0.30001030", - "electron-to-chromium": "^1.3.363", - "node-releases": "^1.1.50" + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" } }, "caniuse-lite": { - "version": "1.0.30001031", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001031.tgz", - "integrity": "sha512-DpAP5a1NGRLgYfaNCaXIRyGARi+3tJA2quZXNNA1Du26VyVkqvy2tznNu5ANyN1Y5aX44QDotZSVSUSi2uMGjg==" - }, - "electron-to-chromium": { - "version": "1.3.413", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.413.tgz", - "integrity": "sha512-Jm1Rrd3siqYHO3jftZwDljL2LYQafj3Kki5r+udqE58d0i91SkjItVJ5RwlJn9yko8i7MOcoidVKjQlgSdd1hg==" + "version": "1.0.30001077", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz", + "integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==" }, "node-releases": { - "version": "1.1.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", - "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", - "requires": { - "semver": "^6.3.0" - } + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==" }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -15730,6 +15221,15 @@ "supports-color": "^5.4.0" } }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -15769,19 +15269,20 @@ }, "dependencies": { "browserslist": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz", - "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", "requires": { - "caniuse-lite": "^1.0.30001030", - "electron-to-chromium": "^1.3.363", - "node-releases": "^1.1.50" + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" } }, "caniuse-lite": { - "version": "1.0.30001031", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001031.tgz", - "integrity": "sha512-DpAP5a1NGRLgYfaNCaXIRyGARi+3tJA2quZXNNA1Du26VyVkqvy2tznNu5ANyN1Y5aX44QDotZSVSUSi2uMGjg==" + "version": "1.0.30001077", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz", + "integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==" }, "dot-prop": { "version": "5.2.0", @@ -15791,53 +15292,25 @@ "is-obj": "^2.0.0" } }, - "electron-to-chromium": { - "version": "1.3.413", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.413.tgz", - "integrity": "sha512-Jm1Rrd3siqYHO3jftZwDljL2LYQafj3Kki5r+udqE58d0i91SkjItVJ5RwlJn9yko8i7MOcoidVKjQlgSdd1hg==" - }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" }, "node-releases": { - "version": "1.1.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", - "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", - "requires": { - "semver": "^6.3.0" - } + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==" }, "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", "requires": { - "dot-prop": "^4.1.1", + "dot-prop": "^5.2.0", "indexes-of": "^1.0.1", "uniq": "^1.0.1" - }, - "dependencies": { - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "requires": { - "is-obj": "^1.0.0" - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" - } } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -15889,42 +15362,30 @@ }, "dependencies": { "browserslist": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz", - "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", "requires": { - "caniuse-lite": "^1.0.30001030", - "electron-to-chromium": "^1.3.363", - "node-releases": "^1.1.50" + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" } }, "caniuse-lite": { - "version": "1.0.30001031", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001031.tgz", - "integrity": "sha512-DpAP5a1NGRLgYfaNCaXIRyGARi+3tJA2quZXNNA1Du26VyVkqvy2tznNu5ANyN1Y5aX44QDotZSVSUSi2uMGjg==" - }, - "electron-to-chromium": { - "version": "1.3.413", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.413.tgz", - "integrity": "sha512-Jm1Rrd3siqYHO3jftZwDljL2LYQafj3Kki5r+udqE58d0i91SkjItVJ5RwlJn9yko8i7MOcoidVKjQlgSdd1hg==" + "version": "1.0.30001077", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz", + "integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==" }, "node-releases": { - "version": "1.1.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", - "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", - "requires": { - "semver": "^6.3.0" - } + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==" }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -15939,12 +15400,25 @@ "postcss-selector-parser": "^3.0.0" }, "dependencies": { + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", "requires": { - "dot-prop": "^4.1.1", + "dot-prop": "^5.2.0", "indexes-of": "^1.0.1", "uniq": "^1.0.1" } @@ -16160,42 +15634,30 @@ }, "dependencies": { "browserslist": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz", - "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", "requires": { - "caniuse-lite": "^1.0.30001030", - "electron-to-chromium": "^1.3.363", - "node-releases": "^1.1.50" + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" } }, "caniuse-lite": { - "version": "1.0.30001031", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001031.tgz", - "integrity": "sha512-DpAP5a1NGRLgYfaNCaXIRyGARi+3tJA2quZXNNA1Du26VyVkqvy2tznNu5ANyN1Y5aX44QDotZSVSUSi2uMGjg==" - }, - "electron-to-chromium": { - "version": "1.3.413", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.413.tgz", - "integrity": "sha512-Jm1Rrd3siqYHO3jftZwDljL2LYQafj3Kki5r+udqE58d0i91SkjItVJ5RwlJn9yko8i7MOcoidVKjQlgSdd1hg==" + "version": "1.0.30001077", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz", + "integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==" }, "node-releases": { - "version": "1.1.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", - "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", - "requires": { - "semver": "^6.3.0" - } + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==" }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -16267,37 +15729,25 @@ }, "dependencies": { "browserslist": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz", - "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", "requires": { - "caniuse-lite": "^1.0.30001030", - "electron-to-chromium": "^1.3.363", - "node-releases": "^1.1.50" + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" } }, "caniuse-lite": { - "version": "1.0.30001031", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001031.tgz", - "integrity": "sha512-DpAP5a1NGRLgYfaNCaXIRyGARi+3tJA2quZXNNA1Du26VyVkqvy2tznNu5ANyN1Y5aX44QDotZSVSUSi2uMGjg==" - }, - "electron-to-chromium": { - "version": "1.3.413", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.413.tgz", - "integrity": "sha512-Jm1Rrd3siqYHO3jftZwDljL2LYQafj3Kki5r+udqE58d0i91SkjItVJ5RwlJn9yko8i7MOcoidVKjQlgSdd1hg==" + "version": "1.0.30001077", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz", + "integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==" }, "node-releases": { - "version": "1.1.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", - "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", - "requires": { - "semver": "^6.3.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==" } } }, @@ -16320,20 +15770,13 @@ } }, "postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", "requires": { - "cssesc": "^2.0.0", + "cssesc": "^3.0.0", "indexes-of": "^1.0.1", "uniq": "^1.0.1" - }, - "dependencies": { - "cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" - } } }, "postcss-svgo": { @@ -16365,9 +15808,9 @@ } }, "postcss-value-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", - "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" }, "prelude-ls": { "version": "1.1.2", @@ -16516,14 +15959,6 @@ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -16567,6 +16002,16 @@ } } }, + "proper-lockfile": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.1.tgz", + "integrity": "sha512-1w6rxXodisVpn7QYvLk706mzprPTAPCYAqxMvctmPN3ekuRk/kuGkGc82pangZiAt4R3lwSuUzheTTn0/Yb7Zg==", + "requires": { + "graceful-fs": "^4.1.11", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "property-expr": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.2.tgz", @@ -16613,6 +16058,13 @@ "parse-asn1": "^5.0.0", "randombytes": "^2.0.1", "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } } }, "pump": { @@ -16758,6 +16210,11 @@ "prop-types": "^15.6.2" } }, + "react-animated-slider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-animated-slider/-/react-animated-slider-2.0.0.tgz", + "integrity": "sha512-JOvLvqAfdNrYjPJWnCZbtS7d/DxSowDTFeIbaS5xUDEVJe3whgxUD6rDZ6+VI0Rhp9N03ogdpuNKlFJIoPhIyg==" + }, "react-apexcharts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.3.7.tgz", @@ -17063,6 +16520,14 @@ } } }, + "react-dropzone-uploader": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/react-dropzone-uploader/-/react-dropzone-uploader-2.11.0.tgz", + "integrity": "sha512-1DpdPMGKP7vYL5SeCh13HCl+Xrz0F6jGrDPU5Tj2ojEIXGMCtfflrZhyXdr7u40IkQ+hYjAUEEtJW24SiY8WRA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, "react-error-overlay": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-3.0.0.tgz", @@ -17099,9 +16564,9 @@ "integrity": "sha512-OicuetMsvuWaTMZqmOAszsqky56hZIirOA6YSVUGyL4IFDkubgqFoWmdEHFRjv3hly8KHEaPFkhZrRrIkUN4ww==" }, "react-hot-loader": { - "version": "4.12.19", - "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.19.tgz", - "integrity": "sha512-p8AnA4QE2GtrvkdmqnKrEiijtVlqdTIDCHZOwItkI9kW51bt5XnQ/4Anz8giiWf9kqBpEQwsmnChDCAFBRyR/Q==", + "version": "4.12.21", + "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.21.tgz", + "integrity": "sha512-Ynxa6ROfWUeKWsTHxsrL2KMzujxJVPjs385lmB2t5cHUxdoRPGind9F00tOkdc1l5WBleOF4XEAMILY1KPIIDA==", "requires": { "fast-levenshtein": "^2.0.6", "global": "^4.3.0", @@ -17746,6 +17211,14 @@ "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" }, + "rifm": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rifm/-/rifm-0.7.0.tgz", + "integrity": "sha512-DSOJTWHD67860I5ojetXdEQRIBvF6YcpNe53j0vn1vp9EUb9N80EiZTxgP+FkDKorWC8PZw052kTF4C1GOivCQ==", + "requires": { + "@babel/runtime": "^7.3.1" + } + }, "rimraf": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", @@ -17895,12 +17368,26 @@ } }, "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } } }, "screenfull": { @@ -17909,9 +17396,9 @@ "integrity": "sha512-cCF2b+L/mnEiORLN5xSAz6H3t18i2oHh9BA8+CQlAh5DRw2+NFAGQJOSYbcGw8B2k04g/lVvFcfZ83b3ysH5UQ==" }, "scroll-behavior": { - "version": "0.9.11", - "resolved": "https://registry.npmjs.org/scroll-behavior/-/scroll-behavior-0.9.11.tgz", - "integrity": "sha512-Eo32cg2uFiQwEiJXHHoTfXLybTlyA1O3ZOIiTz8EqRWie+ExL+7l8PcejhKT+5QmRtykXSlsTRVDC3BVB3S/bg==", + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/scroll-behavior/-/scroll-behavior-0.9.12.tgz", + "integrity": "sha512-18sirtyq1P/VsBX6O/vgw20Np+ngduFXEMO4/NDFXabdOKBL2kjPVUpz1y0+jm99EWwFJafxf5/tCyMeXt9Xyg==", "requires": { "dom-helpers": "^3.4.0", "invariant": "^2.2.4" @@ -17991,9 +17478,12 @@ } }, "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "requires": { + "randombytes": "^2.1.0" + } }, "serve-index": { "version": "1.9.1", @@ -18202,9 +17692,9 @@ } }, "slugify": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.3.6.tgz", - "integrity": "sha512-wA9XS475ZmGNlEnYYLPReSfuz/c3VQsEMoU43mi6OnKMCdbnFXd4/Yg7J0lBv8jkPolacMpOrWEaoYxuE1+hoQ==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.0.tgz", + "integrity": "sha512-FtLNsMGBSRB/0JOE2A0fxlqjI6fJsgHGS13iTuVT28kViI4JjUiNqp/vyis0ZXYcMnpR3fzGNkv+6vRlI2GwdQ==" }, "snapdragon": { "version": "0.8.2", @@ -18411,9 +17901,9 @@ } }, "socket.io-parser": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz", - "integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", + "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==", "requires": { "component-emitter": "1.2.1", "debug": "~4.1.0", @@ -18441,12 +17931,13 @@ } }, "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", + "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", "requires": { "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" + "uuid": "^3.4.0", + "websocket-driver": "0.6.5" }, "dependencies": { "faye-websocket": { @@ -18456,6 +17947,14 @@ "requires": { "websocket-driver": ">=0.5.1" } + }, + "websocket-driver": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "requires": { + "websocket-extensions": ">=0.1.1" + } } } }, @@ -18572,9 +18071,9 @@ "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" }, "spdy": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", - "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "requires": { "debug": "^4.1.0", "handle-thing": "^2.0.0", @@ -18615,9 +18114,9 @@ } }, "readable-stream": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", - "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -18977,19 +18476,20 @@ }, "dependencies": { "browserslist": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz", - "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", "requires": { - "caniuse-lite": "^1.0.30001030", - "electron-to-chromium": "^1.3.363", - "node-releases": "^1.1.50" + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" } }, "caniuse-lite": { - "version": "1.0.30001031", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001031.tgz", - "integrity": "sha512-DpAP5a1NGRLgYfaNCaXIRyGARi+3tJA2quZXNNA1Du26VyVkqvy2tznNu5ANyN1Y5aX44QDotZSVSUSi2uMGjg==" + "version": "1.0.30001077", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz", + "integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==" }, "dot-prop": { "version": "5.2.0", @@ -18999,53 +18499,25 @@ "is-obj": "^2.0.0" } }, - "electron-to-chromium": { - "version": "1.3.413", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.413.tgz", - "integrity": "sha512-Jm1Rrd3siqYHO3jftZwDljL2LYQafj3Kki5r+udqE58d0i91SkjItVJ5RwlJn9yko8i7MOcoidVKjQlgSdd1hg==" - }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" }, "node-releases": { - "version": "1.1.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", - "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", - "requires": { - "semver": "^6.3.0" - } + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==" }, "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", "requires": { - "dot-prop": "^4.1.1", + "dot-prop": "^5.2.0", "indexes-of": "^1.0.1", "uniq": "^1.0.1" - }, - "dependencies": { - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "requires": { - "is-obj": "^1.0.0" - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" - } } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -19185,9 +18657,9 @@ } }, "css-what": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", - "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.3.0.tgz", + "integrity": "sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==" }, "domutils": { "version": "1.7.0", @@ -19273,9 +18745,9 @@ } }, "terser": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", - "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.7.0.tgz", + "integrity": "sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw==", "requires": { "commander": "^2.20.0", "source-map": "~0.6.1", @@ -19290,15 +18762,15 @@ } }, "terser-webpack-plugin": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", - "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz", + "integrity": "sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA==", "requires": { "cacache": "^12.0.2", "find-cache-dir": "^2.1.0", "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", + "serialize-javascript": "^3.1.0", "source-map": "^0.6.1", "terser": "^4.1.2", "webpack-sources": "^1.4.0", @@ -19391,6 +18863,19 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "tmp": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "requires": { + "os-tmpdir": "~1.0.1" + } + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -19501,9 +18986,9 @@ "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==" }, "ts-pnp": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.5.tgz", - "integrity": "sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", + "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==" }, "ts-toolbelt": { "version": "6.5.1", @@ -19589,11 +19074,6 @@ "is-typedarray": "^1.0.0" } }, - "ua-parser-js": { - "version": "0.7.21", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", - "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==" - }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -20121,27 +19601,144 @@ } }, "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "requires": { "loose-envify": "^1.0.0" } }, - "watchpack": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", - "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==", + "watchpack": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz", + "integrity": "sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g==", + "requires": { + "chokidar": "^3.4.0", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "optional": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "optional": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "optional": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", + "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "optional": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "optional": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "optional": true + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "optional": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "optional": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "optional": true + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "optional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "optional": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "watchpack-chokidar2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", + "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", + "optional": true, "requires": { - "chokidar": "^2.1.8", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" + "chokidar": "^2.1.8" }, "dependencies": { "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "optional": true, "requires": { "anymatch": "^2.0.0", "async-each": "^1.0.1", @@ -20160,7 +19757,8 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "optional": true } } }, @@ -20247,9 +19845,9 @@ } }, "webpack-dev-server": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.1.tgz", - "integrity": "sha512-AGG4+XrrXn4rbZUueyNrQgO4KGnol+0wm3MPdqGLmmA+NofZl3blZQKxZ9BND6RDNuvAK9OMYClhjOSnxpWRoA==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", + "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", "requires": { "ansi-html": "0.0.7", "bonjour": "^3.5.0", @@ -20259,33 +19857,38 @@ "debug": "^4.1.1", "del": "^4.1.1", "express": "^4.17.1", - "html-entities": "^1.2.1", + "html-entities": "^1.3.1", "http-proxy-middleware": "0.19.1", "import-local": "^2.0.0", "internal-ip": "^4.3.0", "ip": "^1.1.5", "is-absolute-url": "^3.0.3", "killable": "^1.0.1", - "loglevel": "^1.6.6", + "loglevel": "^1.6.8", "opn": "^5.5.0", "p-retry": "^3.0.1", - "portfinder": "^1.0.25", + "portfinder": "^1.0.26", "schema-utils": "^1.0.0", "selfsigned": "^1.10.7", "semver": "^6.3.0", "serve-index": "^1.9.1", - "sockjs": "0.3.19", + "sockjs": "0.3.20", "sockjs-client": "1.4.0", - "spdy": "^4.0.1", + "spdy": "^4.0.2", "strip-ansi": "^3.0.1", "supports-color": "^6.1.0", "url": "^0.11.0", "webpack-dev-middleware": "^3.7.2", "webpack-log": "^2.0.0", "ws": "^6.2.1", - "yargs": "12.0.5" + "yargs": "^13.3.2" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -20313,26 +19916,6 @@ "upath": "^1.1.1" } }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -20363,11 +19946,6 @@ "original": "^1.0.0" } }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" - }, "globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", @@ -20387,14 +19965,6 @@ } } }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", @@ -20418,11 +19988,6 @@ "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" - }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -20469,35 +20034,34 @@ } } }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" }, "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "ansi-regex": "^4.1.0" } } } }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + }, "ws": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", @@ -20507,28 +20071,26 @@ } }, "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", + "cliui": "^5.0.0", "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", + "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^2.0.0", + "string-width": "^3.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" } }, "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -20586,19 +20148,19 @@ "integrity": "sha512-pxqzFE055NlNTlNyfDG3xlB2QwT1EWdm/CF5dCJI/e+rRHVxrWhWg1rf1lfsWhI1/EePv8gi/A36YxO/+u0FgQ==" }, "websocket-driver": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", - "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "requires": { - "http-parser-js": ">=0.4.0 <0.4.11", + "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" }, "whatwg-encoding": { "version": "1.0.5", @@ -20609,11 +20171,6 @@ "iconv-lite": "0.4.24" } }, - "whatwg-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" - }, "whatwg-mimetype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", @@ -20991,51 +20548,134 @@ } }, "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^3.0.0", + "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" + "yargs-parser": "^18.1.1" }, "dependencies": { "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "find-up": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } } } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -21047,10 +20687,13 @@ "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" }, "yoga-layout-prebuilt": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.9.3.tgz", - "integrity": "sha512-9SNQpwuEh2NucU83i2KMZnONVudZ86YNcFk9tq74YaqrQfgJWO3yB9uzH1tAg8iqh5c9F5j0wuyJ2z72wcum2w==", - "optional": true + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.9.6.tgz", + "integrity": "sha512-Wursw6uqLXLMjBAO4SEShuzj8+EJXhCF71/rJ7YndHTkRAYSU0GY3OghRqfAk9HPUAAFMuqp3U1Wl+01vmGRQQ==", + "optional": true, + "requires": { + "@types/yoga-layout": "1.9.2" + } }, "yup": { "version": "0.28.3", @@ -21108,9 +20751,9 @@ }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "ansi-styles": { "version": "4.2.1", @@ -21153,9 +20796,9 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "inquirer": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.6.tgz", - "integrity": "sha512-7SVO4h+QIdMq6XcqIqrNte3gS5MzCCKZdsq9DO4PJziBFNYzP3PGFbDjgadDb//MCahzgjCxvQ/O2wa7kx9o4w==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", "requires": { "ansi-escapes": "^4.2.1", "chalk": "^3.0.0", @@ -21172,11 +20815,6 @@ "through": "^2.3.6" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, "chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -21202,12 +20840,9 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "run-async": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", - "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", - "requires": { - "is-promise": "^2.1.0" - } + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" }, "semver": { "version": "6.3.0", @@ -21224,11 +20859,6 @@ "strip-ansi": "^6.0.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -21245,6 +20875,13 @@ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "requires": { "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + } } }, "strip-bom": { diff --git a/frontend/package.json b/frontend/package.json index 0c886016445cdb807bcf9349afe88b2ccf3354dc..14b028df7718ad43d5a8190d44e8efb802621409 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,25 +5,30 @@ "version": "0.1.0", "author": "Kyle Mathews ", "dependencies": { + "@date-io/moment": "^1.3.13", "@fortawesome/fontawesome-svg-core": "^1.2.27", "@fortawesome/free-solid-svg-icons": "^5.13.0", "@fortawesome/react-fontawesome": "^0.1.8", + "@material-ui/core": "^4.9.13", + "@material-ui/pickers": "^3.2.10", "apexcharts": "^3.18.1", "bootstrap": "^4.4.1", - "gatsby": "^2.19.7", + "gatsby": "^2.18.25", "gatsby-plugin-offline": "^3.0.32", "gatsby-plugin-page-creator": "^2.2.4", "gatsby-plugin-react-helmet": "^3.1.21", "gatsby-plugin-root-import": "^2.0.5", "gatsby-source-filesystem": "^2.1.46", "inquirer": "^6.5.2", - "moment": "2.24.0", + "moment": "^2.24.0", "prop-types": "^15.7.2", "react": "^16.13.0", + "react-animated-slider": "^2.0.0", "react-apexcharts": "^1.3.7", "react-bootstrap": "^1.0.0-beta.16", "react-calendar": "^2.19.2", "react-dom": "^16.13.0", + "react-dropzone-uploader": "^2.11.0", "react-google-login": "^5.1.1", "react-helmet": "^5.2.1", "react-hook-form": "^5.3.1", @@ -51,7 +56,7 @@ "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-react": "^7.18.3", "eslint-plugin-react-hooks": "^1.7.0", - "gatsby-cli": "^2.10.2", + "gatsby-cli": "^2.10.13", "identity-obj-proxy": "^3.0.0", "jest": "^25.1.0", "prettier": "^1.19.1", diff --git a/frontend/src/api.js b/frontend/src/api.js index 81fb1d37eeda9198ad916b8e26c9fee376168323..3eb6eddc37c395ce9018805719ddba2ebd266544 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -3,6 +3,35 @@ import { BASE_API_URL } from "./config" axios.defaults.baseURL = BASE_API_URL +axios.interceptors.response.use( + response => { + return response + }, + error => { + const originalRequest = error.config + if ( + error.response.status === 401 && + originalRequest.url === "/auth/refresh/" + ) { + // TODO redirect to login + console.log("DO NOTHING") + } else if (error.response.status === 401 && !originalRequest._retry) { + originalRequest._retry = true + const refreshToken = localStorage.getItem("refresh_token") + return postRefreshToken(refreshToken).then(res => { + if (res.status === 200) { + axios.defaults.headers.common.Authorization = + "Bearer " + res.data.access + localStorage.setItem("refresh_token", res.data.refresh) + return axios(originalRequest) + } + }) + } + + return Promise.reject(error) + } +) + export const getListJadwalDonor = date => axios.get(`/donor/jadwal/?date=${date}`) @@ -20,7 +49,7 @@ export const postRegisterFull = data => export const postUserLogin = (email, password) => axios.post( - "/auth/access/", + "/auth/pair/", { email: email, password: password }, { mode: "cors" } ) @@ -28,6 +57,9 @@ export const postUserLogin = (email, password) => export const postUserLoginOAuth = tokenId => axios.post("auth/access/oauth/", { tokenId: tokenId }, { mode: "cors" }) +export const postRefreshToken = refresh_token => + axios.post("/auth/refresh/", { refresh: refresh_token }, { mode: "cors" }) + export const getUserProfile = () => axios.get("/user/profile/", { mode: "cors", cache: "default" }) @@ -76,6 +108,9 @@ export const getAgendaDonor = shouldReturnDefault => ? Promise.resolve({ data: [] }) : axios.get(`/donor/jadwal/agenda/`) +export const getRiwayatAcaraDonor = page => + axios.get(`/acara-donor/pengajuan/riwayat/${!!page ? "?page=" + page : ""}`) + export const getRiwayatDonor = page => axios.get(`/donor/jadwal/riwayat/${!!page ? "?page=" + page : ""}`) @@ -83,3 +118,11 @@ export const getStokDarah = () => axios.get(`/stok-darah/darah/`) export const postAjukanAcaraDonor = data => axios.post("/acara-donor/pengajuan/", data, { mode: "cors" }) + +export const getListArtikelEdukasi = page => + axios.get("/edukasi/artikel?page=" + page) + +export const getArtikelEdukasi = id => axios.get("/edukasi/artikel/" + id) + +export const getFormulirDaftarDonor = jadwalDonorId => + axios.get("/donor/formulir-daftar/" + jadwalDonorId) diff --git a/frontend/src/components/FirstConsentForm.js b/frontend/src/components/FirstConsentForm.js index 28f55d937304a497456b33ee257301f1bc3abba1..c4e2212e813e5036c882f90c205e0719779cc074 100644 --- a/frontend/src/components/FirstConsentForm.js +++ b/frontend/src/components/FirstConsentForm.js @@ -24,76 +24,52 @@ function FirstConsentForm({ setValue }) {
-
- Merasa sehat pada hari ini? -
+
Merasa sehat pada hari ini?
-
- Sedang minum antibiotik? -
+
Sedang minum antibiotik?
-
- Sedang minum obat lain untuk infeksi? -
+
Sedang minum obat lain untuk infeksi?
-
- - Dalam waktu 48 jam terakhir - -
+
Dalam waktu 48 jam terakhir
-
- - Apakah anda sedang minum aspirin atau obat yang mengandung - aspirin? - -
+
+ Apakah anda sedang minum aspirin atau obat yang mengandung + aspirin? +
-
- - Dalam waktu 1 minggu terakhir - -
+
Dalam waktu 1 minggu terakhir
-
- - Apakah anda mengalami sakit kepala dan demam secara - bersamaan? - -
+
+ Apakah anda mengalami sakit kepala dan demam secara + bersamaan? +
-
- - Dalam waktu 6 minggu terakhir - -
+
Dalam waktu 6 minggu terakhir
-
- - Untuk pendonor darah wanita: Apakah anda saat ini sedang - hamil? - -
+
+ Untuk pendonor darah wanita: Apakah anda saat ini sedang + hamil? +
@@ -104,9 +80,7 @@ function FirstConsentForm({ setValue }) { controlId="formBornPlace" > -
- Kehamilan berapa? -
+
Kehamilan berapa?
)} -
- - Dalam waktu 8 minggu terakhir - -
+
Dalam waktu 8 minggu terakhir
-
- - Apakah anda mendonorkan darah, trombosit, atau plasma? - -
+
+ Apakah anda mendonorkan darah, trombosit, atau plasma? +
-
- Apakah anda menerima vaksinasi atau suntikan lainnya? -
+
Apakah anda menerima vaksinasi atau suntikan lainnya?
-
- - Apakah anda pernah kontak dengan orang yang menerima - vaksinasi smallpox ? - -
+
+ Apakah anda pernah kontak dengan orang yang menerima + vaksinasi smallpox ? +
diff --git a/frontend/src/components/ForthConsentForm.js b/frontend/src/components/ForthConsentForm.js index 058a7134b8a2300d6b417ff9b2df926900babbe4..ad3e433928844e83f2159d164fd2f245395bc9ff 100644 --- a/frontend/src/components/ForthConsentForm.js +++ b/frontend/src/components/ForthConsentForm.js @@ -14,85 +14,61 @@ function ForthConsentForm({ setValue }) {
-
- - Tahun 1980 hingga 1996 - -
+
Tahun 1980 hingga 1996
-
- - Apakah anda tinggal selama 3 bulan atau lebih di Inggris? - -
+
+ Apakah anda tinggal selama 3 bulan atau lebih di Inggris? +
-
- - Apakah anda pernah - -
+
Apakah anda pernah
-
- Mendapat hasil positif untuk tes HIV/AIDS? -
+
Mendapat hasil positif untuk tes HIV/AIDS?
-
- - Menggunakan jarum suntik untuk obat-obatan, Steroid yang - tidak diresepkan dokter? - -
+
+ Menggunakan jarum suntik untuk obat-obatan, Steroid yang + tidak diresepkan dokter? +
-
- Menggunakan konsentrat faktor pembekuan? -
+
Menggunakan konsentrat faktor pembekuan?
-
- Menderita hepatitis? -
+
Menderita hepatitis?
-
- Menderita malaria? -
+
Menderita malaria?
-
- Menderita kanker termasuk leukimia? -
+
Menderita kanker termasuk leukimia?
-
- Bermasalah dengan jantung atau paru-paru? -
+
Bermasalah dengan jantung atau paru-paru?
-
- - Menderita pendarahan atau penyakit berhubungan dengan - darah? - -
+
+ Menderita pendarahan atau penyakit berhubungan dengan darah? +
-
- - Berhubungan seksual dengan orang yang tinggal di Afrika? - -
+
+ Berhubungan seksual dengan orang yang tinggal di Afrika? +
-
- Tinggal di Afrika? -
+
Tinggal di Afrika?
diff --git a/frontend/src/components/ModalChangeProfile.js b/frontend/src/components/ModalChangeProfile.js index 1b555a914759e258909cc44586a1a8ddccbaab09..67c39d0e8dc8cf941291402e4aa79259b5626e3d 100644 --- a/frontend/src/components/ModalChangeProfile.js +++ b/frontend/src/components/ModalChangeProfile.js @@ -5,6 +5,7 @@ import { ProfileSchema } from "./form-validation-schema" import ModalCompleteProfile from "./complete-profile" import { useAuth } from "../hooks/authenticate" import { withAuthenticated } from "./authenticated-only" +import moment from "moment" const ModalChangeProfile = ({ show, handleClose }) => { const { user, getAndSetUserProfile } = useAuth() @@ -19,6 +20,8 @@ const ModalChangeProfile = ({ show, handleClose }) => { }, [reset, user]) const onSubmit = data => { + data.birthdate = moment(data.birthdate).format("YYYY-MM-DD") + putUserProfile(data) .then(() => { window.alert("Data berhasil diubah.") diff --git a/frontend/src/components/ModalEventDonorSubmission.js b/frontend/src/components/ModalEventDonorSubmission.js index aa417b3657f09d26d1ef7cfb132c32cced729e18..dd5e6cd9a669f85a3f0d4c0537190d8a7c2cd115 100644 --- a/frontend/src/components/ModalEventDonorSubmission.js +++ b/frontend/src/components/ModalEventDonorSubmission.js @@ -1,20 +1,51 @@ -import React from "react" -import { Modal, Button, Form, Row } from "react-bootstrap" +import React, { useEffect } from "react" +import { Modal, Button, Form, Row, Col } from "react-bootstrap" import { useForm, Controller } from "react-hook-form" -import { EventSubmissionSchema } from "./form-validation-schema" +import { EventSubmissionSchema, MAX_IMAGE_SIZE } from "./form-validation-schema" import { postAjukanAcaraDonor } from "../api" +import { ThemeProvider } from "@material-ui/core/styles" +import theme from "../styles/theme" +import MomentUtils from "@date-io/moment" +import { + KeyboardDatePicker, + KeyboardTimePicker, + MuiPickersUtilsProvider, +} from "@material-ui/pickers" import PhoneInput from "react-phone-input-2" import "react-phone-input-2/lib/style.css" import "./user-form.css" +import Dropzone from "react-dropzone-uploader" +import "react-dropzone-uploader/dist/styles.css" +import "./dropzone.css" +import moment from "moment" const ModalEventDonorSubmission = ({ show, handleClose }) => { - const { control, register, handleSubmit, errors } = useForm({ + const { control, register, setValue, handleSubmit, errors } = useForm({ mode: "onChange", validationSchema: EventSubmissionSchema, + defaultValues: { + tanggal_donor: moment(), + jam_mulai: moment(), + jam_selesai: moment(), + }, }) const onSubmit = async data => { - await postAjukanAcaraDonor(data) + const tanggal_donor = moment(data.tanggal_donor).format("YYYY-MM-DD") + const jam_mulai = moment(data.jam_mulai).format("HH:mm") + const jam_selesai = moment(data.jam_selesai).format("HH:mm") + data["waktu_mulai"] = `${tanggal_donor}T${jam_mulai}` + data["waktu_berakhir"] = `${tanggal_donor}T${jam_selesai}` + delete data["tanggal_donor"] + delete data["jam_mulai"] + delete data["jam_selesai"] + + const formData = new FormData() + Object.keys(data).forEach(key => { + if (typeof data[key] != "undefined") formData.append(key, data[key]) + }) + + await postAjukanAcaraDonor(formData) .then(() => { window.alert("Pengajuan acara donor telah disimpan.") handleClose() @@ -28,377 +59,569 @@ const ModalEventDonorSubmission = ({ show, handleClose }) => { }) } - { - return ( - - + useEffect(() => { + register({ name: "foto_lokasi" }) + }, [register]) - -

- Formulir Pengajuan Acara Donor Darah -

+ return ( + + + + -
-
- Informasi Penyelenggara -
+ +

+ Formulir Pengajuan Acara Donor Darah +

- -
- -
- Nama - * -
-
-
-
- - {errors.nama_institusi && ( - - {errors.nama_institusi.message} - - )} -
-
+
+
+ Informasi Penyelenggara +
- -
- -
- Alamat Institusi - * -
-
-
-
- - {errors.alamat_institusi && ( - - {errors.alamat_institusi.message} - - )} -
-
+ +
+ +
+ Nama Institusi + * +
+
+
+
+ + {errors.nama_institusi && ( + + {errors.nama_institusi.message} + + )} +
+
- -
- -
- Email Kantor - * -
-
-
-
- - {errors.email_kantor && ( - - {errors.email_kantor.message} - - )} -
-
+ +
+ +
+ Alamat Institusi + * +
+
+
+
+ + {errors.alamat_institusi && ( + + {errors.alamat_institusi.message} + + )} +
+
- -
- -
- No. Telepon Kantor - * -
-
-
-
- - } - name="no_telp_kantor" - control={control} - /> - {errors.no_telp_kantor && ( - - {errors.no_telp_kantor.message} - - )} -
-
+ +
+ +
+ Email Kantor + * +
+
+
+
+ + {errors.email_kantor && ( + + {errors.email_kantor.message} + + )} +
+
-
- Kontak Koordinator -
+ +
+ +
+ No. Telepon Kantor + * +
+
+
+
+ + } + name="no_telp_kantor" + control={control} + /> + {errors.no_telp_kantor && ( + + {errors.no_telp_kantor.message} + + )} +
+
- -
- -
- Nama Koordinator - * -
-
-
-
- - {errors.nama_koor && ( - - {errors.nama_koor.message} - - )} -
-
+
+ Kontak Koordinator +
- -
- -
- Email Koordinator - * -
-
-
-
- - {errors.email_koor && ( - - {errors.email_koor.message} - - )} -
-
+ +
+ +
+ Nama Koordinator + * +
+
+
+
+ + {errors.nama_koor && ( + + {errors.nama_koor.message} + + )} +
+
- -
- -
- No. Telepon Koordinator - * -
-
-
-
- - } - name="no_telp_koor" - control={control} - /> - {errors.no_telp_koor && ( - - {errors.no_telp_koor.message} - - )} -
-
+ +
+ +
+ Email Koordinator + * +
+
+
+
+ + {errors.email_koor && ( + + {errors.email_koor.message} + + )} +
+
-
- Informasi Acara Donor -
+ +
+ +
+ No. Telepon Koordinator + * +
+
+
+
+ + } + name="no_telp_koor" + control={control} + /> + {errors.no_telp_koor && ( + + {errors.no_telp_koor.message} + + )} +
+
- -
- -
- Alamat Lokasi Donor - * -
-
-
-
- - {errors.alamat_lokasi_donor && ( - - {errors.alamat_lokasi_donor.message} - - )} -
-
+
+ Informasi Acara Donor +
- -
- -
- Waktu Donor - * -
-
-
-
- - {errors.waktu_donor && ( - - {errors.waktu_donor.message} - - )} -
-
+ +
+ +
+ Kategori + * +
+
+
+
+ + + + +
+
- -
- -
- Perkiraan Jumlah Donor - * -
-
-
-
- - {errors.perkiraan_jumlah_donor && ( - - {errors.perkiraan_jumlah_donor.message} - - )} -
-
+ +
+ +
+ Alamat Lokasi + * +
+
+
+
+ + {errors.alamat_lokasi_donor && ( + + {errors.alamat_lokasi_donor.message} + + )} +
+
- -
- -
Keterangan
-
-
-
- -
-
+ +
+ +
+ Kecamatan + * +
+
+
+
+ + + + + + + + + + + + + +
+
-
- +
+ +
+ Tanggal Pelaksanaan + * +
+
+
+
+ + } + name="tanggal_donor" + control={control} + /> + {errors.tanggal_donor && ( + + {errors.tanggal_donor.message} + + )} +
+ + + + + + +
+ Jam Mulai + * +
+
+ + } + name="jam_mulai" + control={control} + /> + {errors.jam_mulai && ( + + {errors.jam_mulai.message} + + )} +
+ + + + +
+ Jam Berakhir + * +
+
+
+ + } + name="jam_selesai" + control={control} + /> + {errors.jam_selesai && ( + + {errors.jam_selesai.message} + + )} +
+ +
+ + +
+ +
+ Perkiraan Jumlah Donor + * +
+
+
+
+ + {errors.perkiraan_jumlah_donor && ( + + {errors.perkiraan_jumlah_donor.message} + + )} +
+
+ + +
+ +
Keterangan
+
+
+
+ +
+
+ + +
+ +
Foto Lokasi
+
+
+
+ + setValue("foto_lokasi", file) + } + accept="image/*" + maxSizeBytes={MAX_IMAGE_SIZE} + inputContent="Seret dan Lepas atau Tekan untuk Memilih Sebuah Foto" + inputProps={{ id: "formImg" }} + inputRef={register} + /> + } + name="foto_lokasi" + control={control} + /> + {errors.foto_lokasi && ( + + {errors.foto_lokasi.message} + + )} +
+
+ +
+ +
-
-
- - ) - } + + + + + ) } export default ModalEventDonorSubmission diff --git a/frontend/src/components/ModalEventDonorSubmissionHelper.js b/frontend/src/components/ModalEventDonorSubmissionHelper.js index 43f16dbcb6bb667dead72f5f8598382b84604a57..a6e73dca24378afd5ec467c61375bbfdfc9490fd 100644 --- a/frontend/src/components/ModalEventDonorSubmissionHelper.js +++ b/frontend/src/components/ModalEventDonorSubmissionHelper.js @@ -1,10 +1,10 @@ import { fireEvent, screen } from "@testing-library/react" +import moment from "moment" export const fillEventDonorSubmission = async () => { expect(await screen.getByLabelText(/Alamat Institusi/i)).toBeInTheDocument() - expect( - await screen.getByLabelText(/Alamat Lokasi Donor/i) - ).toBeInTheDocument() + expect(await screen.getByText(/Informasi Acara Donor/i)).toBeInTheDocument() + fireEvent.change(screen.getByPlaceholderText(/Nama Institusi\/Organisasi/i), { target: { value: "Pacilkom", @@ -25,6 +25,7 @@ export const fillEventDonorSubmission = async () => { value: "+6281234535434", }, }) + fireEvent.change(screen.getByLabelText(/Nama Koordinator/i), { target: { value: "M. Fairuzi", @@ -40,14 +41,44 @@ export const fillEventDonorSubmission = async () => { value: "+6281234535434", }, }) - fireEvent.change(screen.getByLabelText(/Alamat Lokasi Donor/i), { + + fireEvent.change(screen.getByLabelText(/Kategori/i), { + target: { + value: "Terbuka", + }, + }) + fireEvent.change(screen.getByLabelText(/Alamat Lokasi/i), { target: { value: "Pacilkom", }, }) - fireEvent.change(screen.getByLabelText(/Waktu Donor/i), { + fireEvent.change(screen.getByLabelText(/Kecamatan/i), { + target: { + value: "Beji", + }, + }) + fireEvent.change(screen.getByLabelText(/Tanggal Pelaksanaan/i), { + target: { + value: moment() + .endOf("day") + .add(1, "seconds") + .format("DD/MM/YYYY"), + }, + }) + fireEvent.change(screen.getByLabelText(/Jam Mulai/i), { + target: { + value: moment() + .endOf("day") + .add(5, "hours") + .format("HH:mm"), + }, + }) + fireEvent.change(screen.getByLabelText(/Jam Berakhir/i), { target: { - value: "2040-05-07T12:23", + value: moment() + .endOf("day") + .add(7, "hours") + .format("HH:mm"), }, }) fireEvent.change(screen.getByLabelText(/Perkiraan Jumlah Donor/i), { @@ -55,4 +86,12 @@ export const fillEventDonorSubmission = async () => { value: "666", }, }) + fireEvent.change( + screen.getByText("Seret dan Lepas atau Tekan untuk Memilih Sebuah Foto"), + { + target: { + files: [new File(["/(≧ x ≦)\"], "foto.png", { type: "image/png" })], + }, + } + ) } diff --git a/frontend/src/components/ModalGoogleCompleteProfile.js b/frontend/src/components/ModalGoogleCompleteProfile.js index 1f2bfeb2044901bc718be1344c85c94cfd5efb9e..e566201c466ce0f9448eb0a77edf30fe722dc2df 100644 --- a/frontend/src/components/ModalGoogleCompleteProfile.js +++ b/frontend/src/components/ModalGoogleCompleteProfile.js @@ -5,8 +5,9 @@ import { postUserProfile } from "../api" import { ProfileSchema } from "./form-validation-schema" import ModalCompleteProfile from "./complete-profile" import { withAuthenticated } from "./authenticated-only" +import moment from "moment" -const ModalGoogleCompleteProfile = ({ show, handleClose }) => { +const ModalGoogleCompleteProfile = ({ show, handleClose, handleSuccess }) => { const { user, logout, getAndSetUserProfile } = useAuth() const { control, register, handleSubmit, errors } = useForm({ @@ -20,11 +21,13 @@ const ModalGoogleCompleteProfile = ({ show, handleClose }) => { }) const onSubmit = async data => { + data.birthdate = moment(data.birthdate).format("YYYY-MM-DD") + await postUserProfile(data) - .then(() => { + .then(async () => { window.alert("Profil Anda sudah disimpan.") - getAndSetUserProfile() - handleClose() + const newUser = await getAndSetUserProfile() + handleSuccess(newUser) }) .catch(error => { if (error.response) { diff --git a/frontend/src/components/ModalLogin.js b/frontend/src/components/ModalLogin.js index 8ba66eefcc789baf138e3a6c937ee63108e889e3..19fdefe38c9847681fbb16f58094104d3f7407ee 100644 --- a/frontend/src/components/ModalLogin.js +++ b/frontend/src/components/ModalLogin.js @@ -1,5 +1,5 @@ import React, { useState } from "react" -import { Button, Form, Modal } from "react-bootstrap" +import { Button, Form, Modal, OverlayTrigger, Tooltip } from "react-bootstrap" import { GoogleLogin } from "react-google-login" import { useForm } from "react-hook-form" import { useAuth } from "../hooks/authenticate" @@ -16,8 +16,8 @@ const ModalLogin = ({ show, handleClose, handleSuccess }) => { setLoginError(null) setIsLoadingLogin(true) try { - await login(data.email, data.password) - handleSuccess() + const user = await login(data.email, data.password) + handleSuccess(user) } catch (error) { const message = error.response ? "Email atau password salah." @@ -34,8 +34,8 @@ const ModalLogin = ({ show, handleClose, handleSuccess }) => { setLoginError(null) setIsLoadingLogin(true) try { - await loginOAuth(data.tokenId) - handleClose() + const user = await loginOAuth(data.tokenId) + handleSuccess(user) } catch (error) { const message = error.response ? error.response.data.detail @@ -141,9 +141,15 @@ const ModalLogin = ({ show, handleClose, handleSuccess }) => {

- + Silakan hubungi admin ;) + } + > + +
diff --git a/frontend/src/components/ModalRegister.js b/frontend/src/components/ModalRegister.js index 199507503e7782378c72c9ed3a2bbf6b42163101..4428b57bb7d1fc49680089dec1f6a19d350dcc06 100644 --- a/frontend/src/components/ModalRegister.js +++ b/frontend/src/components/ModalRegister.js @@ -4,6 +4,7 @@ import { postRegisterFull } from "../api" import { RegisterSchema } from "./form-validation-schema" import ModalCompleteProfile from "./complete-profile" import ModalRegisterAccount from "./ModalRegisterAccount" +import moment from "moment" const ModalRegister = ({ show, handleClose }) => { const [showModalCompleteProfile, setShowModalCompleteProfile] = useState( @@ -27,6 +28,8 @@ const ModalRegister = ({ show, handleClose }) => { }) const onSubmit = async data => { + data.birthdate = moment(data.birthdate).format("YYYY-MM-DD") + await postRegisterFull(data) .then(response => { window.alert( diff --git a/frontend/src/components/SecondConsentForm.js b/frontend/src/components/SecondConsentForm.js index 876eb8d706023e82cf592e49c39d13e605e6e264..990ce7dc1347f129c765bdf5a1f9f38aafd9db5a 100644 --- a/frontend/src/components/SecondConsentForm.js +++ b/frontend/src/components/SecondConsentForm.js @@ -14,51 +14,35 @@ function SecondConsentForm({ setValue }) {
-
- - Dalam waktu 16 minggu terakhir - -
+
Dalam waktu 16 minggu terakhir
-
- - Apakah anda mendonorkan 2 kantung sel darah merah melalui - prose aferesis? - -
+
+ Apakah anda mendonorkan 2 kantung sel darah merah melalui + prose aferesis? +
-
- - Dalam waktu 12 bulan terakhir - -
+
Dalam waktu 12 bulan terakhir
-
- Apakah anda pernah menerima transfusi darah? -
+
Apakah anda pernah menerima transfusi darah?
-
- - Apakah anda pernah menerima transplasi, organ, jaringan, - atau sumsum tulang? - -
+
+ Apakah anda pernah menerima transplasi, organ, jaringan, + atau sumsum tulang? +
-
- Apakah anda pernah cangkok tulang untuk kulit? -
+
Apakah anda pernah cangkok tulang untuk kulit?
-
- Apakah anda pernah tertusuk jarum medis? -
+
Apakah anda pernah tertusuk jarum medis?
-
- - Apakah anda pernah berhubungan seksual dengan orang dengan - HIV/AIDS? - -
+
+ Apakah anda pernah berhubungan seksual dengan orang dengan + HIV/AIDS? +
-
- - Apakah anda pernah berhubungan seksual dengan pekerja seks - komersial? - -
+
+ Apakah anda pernah berhubungan seksual dengan pekerja seks + komersial? +
-
- - Apakah anda pernah berhubungan seksual dengan pengguna - narkoba jarum suntik? - -
+
+ Apakah anda pernah berhubungan seksual dengan pengguna + narkoba jarum suntik? +
-
- - Apakah anda pernah berhubungan seksual dengan pengguna - konsentrat faktor pembekuan? - -
+
+ Apakah anda pernah berhubungan seksual dengan pengguna + konsentrat faktor pembekuan? +
-
- - Donor wanita: Apakah anda pernah berhubungan seksual - dengan laki-laki yang biseksual? - -
+
+ Pendonor wanita: Apakah + anda pernah berhubungan seksual dengan laki-laki yang + biseksual? +
-
- - Apakah anda pernah berhubungan seksual dengan penderita - hepatitis? - -
+
+ Apakah anda pernah berhubungan seksual dengan penderita + hepatitis? +
-
- Apakah anda tinggal bersama penderita hepatitis? -
+
Apakah anda tinggal bersama penderita hepatitis?
-
- Apakah anda memiliki tatto? -
+
Apakah anda memiliki tatto?
-
- - Apakah anda memiliki tindik telinga atau pada bagian tubuh - lainnya? - -
+
+ Apakah anda memiliki tindik telinga atau pada bagian tubuh + lainnya? +
-
- - Apakah anda sedang atau pernah mendapat pengobatan sifilis - atua GO (kencing tanah)? - -
+
+ Apakah anda sedang atau pernah mendapat pengobatan sifilis + atua GO (kencing tanah)? +
-
- - Apakah anda pernah ditahan dipenjara untuk waktu lebih - dari 72 jam? - -
+
+ Apakah anda pernah ditahan dipenjara untuk waktu lebih dari + 72 jam? +
-
- - Dalam waktu 3 tahun - -
+
Dalam waktu 3 tahun
-
- Apakah anda pernah berada di luar wilayah Indonesia? -
+
Apakah anda pernah berada di luar wilayah Indonesia?
-
- - Tahun 1977 hingga sekarang - -
+
Tahun 1977 hingga sekarang
-
- - Apakah anda pernah menerima uang, obat, atau pembayaran - lainnya untuk seks? - -
+
+ Apakah anda pernah menerima uang, obat, atau pembayaran + lainnya untuk seks? +
-
- - Laki-laki: Apakah anda pernah berhubungan seksual dengan - laki-laki, walaupun sekali? - -
+
+ Pendonor laki-laki: + Apakah anda pernah berhubungan seksual dengan laki-laki, + walaupun sekali? +
-
- - Tahun 1980 hingga sekarang - -
+
Tahun 1980 hingga sekarang
-
- - Apakah anda tinggal selama 5 tahun atau lebih di Eropa? - -
+
+ Apakah anda tinggal selama 5 tahun atau lebih di Eropa? +
-
- Apakah anda menerima transfusi darah di Inggris? -
+
Apakah anda menerima transfusi darah di Inggris?
({ + id: idGenerator(), + title: "Test Title", + featured_image: "http://localhost:8080/media/edukasi/placeholder1.jpg", + content: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.", + ...override, +}) diff --git a/frontend/src/components/authenticated-only.js b/frontend/src/components/authenticated-only.js index a53842bac16510f407f544379f46bcb3c92a94b7..db2692af8a006a2486f7d1a8c1acb32dae30186b 100644 --- a/frontend/src/components/authenticated-only.js +++ b/frontend/src/components/authenticated-only.js @@ -1,15 +1,39 @@ import React from "react" import { useAuth } from "../hooks/authenticate" +import { navigate } from "gatsby" const AuthenticatedOnly = ({ Component, ...otherProps }) => { const { user } = useAuth() if (user) return // need to handle loading state - else return
+ else return null } export const withAuthenticated = Component => { return props => } +export const AuthenticatedOrRedirect = ({ + Component, + redirectPath = "/", + ...otherProps +}) => { + const { user } = useAuth() + if (user) return + else { + if (typeof window !== "undefined") navigate(redirectPath) + return null + } +} + +export const withAuthenticatedOrRedirect = (Component, redirectPath) => { + return props => ( + + ) +} + export default AuthenticatedOnly diff --git a/frontend/src/components/authenticated-only.test.js b/frontend/src/components/authenticated-only.test.js new file mode 100644 index 0000000000000000000000000000000000000000..2f188a273ebfb0e1050c252a2cec44e4964cf07b --- /dev/null +++ b/frontend/src/components/authenticated-only.test.js @@ -0,0 +1,21 @@ +import { navigate } from "gatsby" +import React from "react" +import { render } from "../utils/test-util" +import { AuthenticatedOrRedirect } from "./authenticated-only" + +describe(`AuthenticatedOrRedirect`, () => { + it(`should redirect home by default`, () => { + render(} />) + expect(navigate).toHaveBeenCalledWith("/") + }) + + it(`should redirect to redirectPath if given`, () => { + render( + } + redirectPath="/jadwal-donor" + /> + ) + expect(navigate).toHaveBeenCalledWith("/jadwal-donor") + }) +}) diff --git a/frontend/src/components/carousel.js b/frontend/src/components/carousel.js new file mode 100644 index 0000000000000000000000000000000000000000..e1ff94ad84125d6ea80830821ad64143b90e82d5 --- /dev/null +++ b/frontend/src/components/carousel.js @@ -0,0 +1,38 @@ +import { Link } from "gatsby" +import React from "react" +import Slider from "react-animated-slider" +import { useQuery } from "react-query" +import { getListArtikelEdukasi } from "../api" +import ErrorRetry from "../components/error-retry" +import Spinner from "../components/spinner" +import "./slider.css" + +export default () => { + const { status, data, refetch } = useQuery("artikel-edukasi", () => + getListArtikelEdukasi(1) + ) + return ( + <> + {status === "loading" ? ( + + ) : status === "error" ? ( + + ) : ( + + {data.data.results.map((item, index) => ( +
+ +

{item.title}

+ +
+ ))} +
+ )} + + ) +} diff --git a/frontend/src/components/carousel.test.js b/frontend/src/components/carousel.test.js new file mode 100644 index 0000000000000000000000000000000000000000..fa14b9328ada2d6769cd366569fff3e07f980db6 --- /dev/null +++ b/frontend/src/components/carousel.test.js @@ -0,0 +1,24 @@ +import { screen } from "@testing-library/react" +import React from "react" +import { getListArtikelEdukasi } from "../api" +import { render } from "../utils/test-util" +import { artikelEdukasiFactory } from "./artikel-edukasi.factory" +import Slider from "./carousel" +getListArtikelEdukasi.mockResolvedValue({ data: { results: [] } }) + +describe("Carousel", () => { + it("mounts", () => { + const slider = + expect(slider).toBeDefined() + }) + + it("shows image title after load", async () => { + getListArtikelEdukasi.mockResolvedValueOnce({ + data: { + results: [artikelEdukasiFactory({ title: "Test Title" })], + }, + }) + render() + expect(await screen.findByText("Test Title")).toBeInTheDocument() + }) +}) diff --git a/frontend/src/components/complete-profile.js b/frontend/src/components/complete-profile.js index de18ddd93db03e083a96475d20f2f88a6f4a7c86..c29b4ae963f7a2ba85985f8a26bd518aca6cc8e2 100644 --- a/frontend/src/components/complete-profile.js +++ b/frontend/src/components/complete-profile.js @@ -5,6 +5,14 @@ import { Controller } from "react-hook-form" import PhoneInput from "react-phone-input-2" import "react-phone-input-2/lib/style.css" import "./user-form.css" +import { ThemeProvider } from "@material-ui/core/styles" +import theme from "../styles/theme" +import MomentUtils from "@date-io/moment" +import { + KeyboardDatePicker, + MuiPickersUtilsProvider, +} from "@material-ui/pickers" +import moment from "moment" import { useAuth } from "../hooks/authenticate" function CompleteProfile({ @@ -21,526 +29,544 @@ function CompleteProfile({ }) { const { closeModalChangeProfile } = useAuth() return ( - - + + + + - -

- {modalName} -

+ +

+ {modalName} +

-
-

- Informasi Pribadi -

+
+

+ Informasi Pribadi +

- -
- -
- No. KTP/SIM/Passport - * -
-
-
-
- - {errors.id_card_no && ( - - {errors.id_card_no.message} - - )} -
-
- - -
- -
- Nama Lengkap - * -
-
-
-
- - {errors.first_name && ( - - {errors.first_name.message} - - )} -
-
- - -
- -
- Tempat Lahir - * -
-
-
-
- - {errors.birthplace && ( - - {errors.birthplace.message} - - )} -
-
- - -
- -
- Tanggal Lahir - * -
-
-
-
- - {errors.birthdate && ( - - {errors.birthdate.message} - - )} -
-
- - -
- -
- Berat Badan - * -
-
-
-
- - {errors.body_weight && ( - - {errors.body_weight.message} - - )} -
-
- - - -
- Jenis Kelamin - * -
-
- -
-
- + +
+ +
+ No. KTP/SIM/Passport + * +
+
-
- +
+ + {errors.id_card_no && ( + + {errors.id_card_no.message} + + )}
-
- {errors.sex && ( - - {errors.sex.message} - - )} - -
+ - -
- -
- Pekerjaan - * -
-
-
-
- - - - - - - - - -
-
+
+ +
+ Nama Lengkap + * +
+
+
+
+ + {errors.first_name && ( + + {errors.first_name.message} + + )} +
+ - -
- -
- Gol. Darah - * -
-
-
-
- - - - - - -
-
+
+ +
+ Tempat Lahir + * +
+
+
+
+ + {errors.birthplace && ( + + {errors.birthplace.message} + + )} +
+ - -
- -
- Status Perkawinan - * -
-
-
-
- - - - - - -
-
- -

- Alamat -

+
+ +
+ Tanggal Lahir + * +
+
+
+
+ + } + name="birthdate" + control={control} + defaultValue={moment()} + /> + {errors.birthdate && ( + + {errors.birthdate.message} + + )} +
+ - - -
- Alamat Rumah - * -
-
- - - + +
+ +
+ Berat Badan + * +
+
+
+
+ {errors.body_weight && ( + + {errors.body_weight.message} + + )} +
+
+ + + +
+ Jenis Kelamin + * +
+
+ +
+
+ +
+
+ +
+
+ {errors.sex && ( + + {errors.sex.message} + + )}
- {errors.address && ( - - {errors.address.message} - - )} - - + + +
+ +
+ Pekerjaan + * +
+
+
+
- - + > + + + + + + + + +
+
+ + +
+ +
+ Gol. Darah + * +
+
+
+
- - + > + + + + + +
+
+ + +
+ +
+ Status Perkawinan + * +
+
+
+
- + > + + + + + +
- {errors.district && ( - - {errors.district.message} - - )} - {errors.village && ( - - {errors.village.message} - - )} - {errors.city && ( - - {errors.city.message} - - )} - -
- -
- -
Alamat Kantor
-
-
-
- -
-
+

+ Alamat +

-

- Kontak -

+ + +
+ Alamat Rumah + * +
+
+ + + + + + + {errors.address && ( + + {errors.address.message} + + )} + + + + + + + + + + + + {errors.village && ( + + {errors.village.message} + + )} + {errors.district && ( + + {errors.district.message} + + )} + {errors.city && ( + + {errors.city.message} + + )} + +
- -
- -
- No. Telepon/HP - * -
-
-
-
- +
+ +
Alamat Kantor
+
+
+
+ - } - name="phone_no" - control={control} - /> - {errors.phone_no && ( - - {errors.phone_no.message} - - )} -
- +
+
- -
- -
Email Kantor
-
-
-
- - {errors.work_email && ( - - {errors.work_email.message} - - )} -
-
+

+ Kontak +

- -
- -
No. Telepon Kantor
-
-
-
- +
+ +
+ No. Telepon/HP + * +
+
+
+
+ + } + name="phone_no" + control={control} /> - } - name="work_phone_no" - control={control} - /> -
- + {errors.phone_no && ( + + {errors.phone_no.message} + + )} +
+
- {isLoggedIn && ( -
- - -
- )} +
+ +
Email Kantor
+
+
+
+ + {errors.work_email && ( + + {errors.work_email.message} + + )} +
+
- {!isLoggedIn && ( -
- +
+ +
No. Telepon Kantor
+
+
+
+ + } + name="work_phone_no" + control={control} + /> +
+ + + {isLoggedIn && ( +
+ + +
+ )} + + {!isLoggedIn && ( +
+ +
+ )}
- )} -
- - + + + + ) } export default CompleteProfile diff --git a/frontend/src/components/dropzone.css b/frontend/src/components/dropzone.css new file mode 100644 index 0000000000000000000000000000000000000000..f8f75ce9aa2b52f049523f834ca99673cb4455f1 --- /dev/null +++ b/frontend/src/components/dropzone.css @@ -0,0 +1,96 @@ +@import "../styles/global.css"; + +@keyframes shake { + 0% { + transform: translate(2px, 1px) rotate(0deg); + } + 10% { + transform: translate(-1px, -2px) rotate(-1deg); + } + 20% { + transform: translate(-3px, 0px) rotate(1deg); + } + 30% { + transform: translate(0px, 2px) rotate(0deg); + } + 40% { + transform: translate(1px, -1px) rotate(1deg); + } + 50% { + transform: translate(-1px, 2px) rotate(-1deg); + } + 60% { + transform: translate(-3px, 1px) rotate(0deg); + } + 70% { + transform: translate(2px, 1px) rotate(-1deg); + } + 80% { + transform: translate(-1px, -1px) rotate(1deg); + } + 90% { + transform: translate(2px, 2px) rotate(0deg); + } + 100% { + transform: translate(1px, -2px) rotate(-1deg); + } +} + +.dzu-dropzone { + overflow: hidden; + border: 2px solid var(--red); + border-radius: 0.3rem; + border-style: dashed; +} +.dzu-dropzone:hover { + border: 2px solid var(--dark-red); + box-shadow: 0 0 2px 1px var(--dark-red); +} + +.dzu-inputLabel { + color: var(--blue); + font-family: "Poppins", sans-serif; + text-align: center; + font-size: 16px; + font-weight: 500; +} +.dzu-inputLabel:hover { + text-shadow: 0.7px 0 0 currentColor; +} + +.dzu-previewImage { + max-height: 200px; + max-width: 200px; +} + +.dzu-previewContainer { + pointer-events: none; + padding: 20px; + justify-content: center; + border-bottom: 0; + flex-wrap: wrap; + text-align: center; +} + +.dzu-previewContainer span { + color: red; + font-size: 14px; +} + +.dzu-previewStatusContainer { + pointer-events: auto; +} + +.dzu-previewButton { + pointer-events: auto; + background-size: auto; +} + +.dzu-previewContainer:hover { + animation: shake 1s linear infinite; +} + +.dzu-previewFileNameError { + color: var(--blue) !important; + margin-right: 10px; +} diff --git a/frontend/src/components/event-donor-register.css b/frontend/src/components/event-donor-register.css index fae9060e633d0bf3c0120293c19d1ead38ba8dff..9e214d27e143246f3e1cfaf1b38e1133c76d6e65 100644 --- a/frontend/src/components/event-donor-register.css +++ b/frontend/src/components/event-donor-register.css @@ -6,6 +6,17 @@ overflow-y: auto; width: 100%; } +.text-header { + color: var(--blue); + font-style: italic; + font-weight: bold; + text-transform: uppercase; +} +.text-gender { + color: var(--red); + font-weight: bold; + text-transform: uppercase; +} .red-border-field { border: 1px solid var(--red); diff --git a/frontend/src/components/footer.js b/frontend/src/components/footer.js index f7c2768793457b4412b11afddcbcbbb4972bbbb6..88fbf14c5313eca568fc465bb719fdd97132cf09 100644 --- a/frontend/src/components/footer.js +++ b/frontend/src/components/footer.js @@ -1,13 +1,27 @@ import React from "react" +import logo from "src/images/ig.svg" const Footer = () => (
-
{`© ${new Date().getFullYear()} | UTD PMI DEPOK`}
-
- Jl. Boulevard, Grand Depok City Kota Kembang, Jatimulya, Kec. Cilodong, - Kota Depok, Jawa Barat 16413 +
+
+
+
{`© ${new Date().getFullYear()} | UTD PMI DEPOK`}
+
+ Jl. Boulevard Grand Depok City Kota Kembang, Jatimulya, Kec. + Cilodong, Kota Depok, Jawa Barat 16413 +
+
Tel. 021 8792 7210 | 0895 2200 7210
+
+
+
+
+ + Instagram PMI Depok + +
+
-
Tel. 021 8792 7210 | 0895 2200 7210
) diff --git a/frontend/src/components/form-validation-schema.js b/frontend/src/components/form-validation-schema.js index d0bfc7c428734cb181b97799b0cf863b78ab413c..86360587e771cee9a1fef6799a34e8180a8f10be 100644 --- a/frontend/src/components/form-validation-schema.js +++ b/frontend/src/components/form-validation-schema.js @@ -4,20 +4,20 @@ import moment from "moment" const AccountSchema = yup.object().shape({ email: yup .string() - .email("Masukkan email yang valid.") - .required("Masukkan email."), + .required("Masukkan email.") + .email("Masukkan email yang valid."), password: yup .string() - .min(8, "Password terlalu pendek - setidaknya harus 8 karakter.") + .required("Masukkan password.") + .min(9, "Password terlalu pendek - setidaknya harus 9 karakter.") .matches( - /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/, // NOSONAR - "Password harus terdiri dari minimal 1 huruf kecil, 1 huruf kapital, 1 angka, dan 1 karakter spesial (!@#$%^&*)." - ) - .required("Masukkan password."), + /^(?=.*[a-zA-Z])(?=.*\d)[\w.,/\\+\-!?@#$%^&* ]{9,}$/, // NOSONAR + "Password harus terdiri dari minimal 1 huruf atau 1 angka." + ), passwordConfirmation: yup .string() - .oneOf([yup.ref("password"), null], "Password harus sama.") - .required("Masukkan konfirmasi password."), + .required("Masukkan konfirmasi password.") + .oneOf([yup.ref("password"), null], "Password harus sama."), }) export const ProfileSchema = yup.object().shape({ @@ -41,14 +41,12 @@ export const ProfileSchema = yup.object().shape({ .matches(/^\d{16}$/, "Masukkan nomor kartu pengenal yang valid."), birthplace: yup.string().required("Masukkan tempat kelahiran."), birthdate: yup - .string() + .date() .required("Masukkan tanggal lahir.") - .test("is-the-right-format", "Masukkan tanggal lahir.", function(value) { - return moment(new Date(value), "YYYY-MM-DD", true).isValid() - }) + .typeError("Masukkan tanggal yang valid.") .test( "is-the-legal-age", - "Mohon maaf, usia Anda tidak memenuhi persyaratan.", + "Mohon maaf, usia Anda tidak memenuhi persyaratan. Usia harus dalam rentang 17-60 tahun.", function(value) { let age = moment().diff(moment(new Date(value), "YYYY-MM-DD"), "years") return age >= 17 && age <= 60 @@ -72,10 +70,10 @@ export const ChangePasswordSchema = yup.object().shape({ new_password: yup .string() .required("Masukkan password.") - .min(8, "Password terlalu pendek - setidaknya harus 8 karakter.") + .min(9, "Password terlalu pendek - setidaknya harus 9 karakter.") .matches( - /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/, // NOSONAR - "Password harus terdiri dari minimal 1 huruf kecil, 1 huruf kapital, 1 angka, dan 1 karakter spesial (!@#$%^&*)." + /^(?=.*[a-zA-Z])(?=.*\d)[\w.,/\\+\-!?@#$%^&* ]{9,}$/, // NOSONAR + "Password harus terdiri dari minimal 1 huruf atau 1 angka." ), confirm_password: yup .string() @@ -83,6 +81,9 @@ export const ChangePasswordSchema = yup.object().shape({ .oneOf([yup.ref("new_password"), null], "Password harus sama."), }) +const SUPPORTED_FORMATS = ["image/jpg", "image/jpeg", "image/gif", "image/png"] +export const MAX_IMAGE_SIZE = 52428800 + export const EventSubmissionSchema = yup.object().shape({ nama_institusi: yup.string().required("Masukkan nama institusi."), alamat_institusi: yup.string().required("Masukkan alamat institusi."), @@ -104,19 +105,43 @@ export const EventSubmissionSchema = yup.object().shape({ .email("Masukkan email yang valid."), no_telp_koor: yup.number().required("Masukkan nomor telepon koordinator."), alamat_lokasi_donor: yup.string().required("Masukkan alamat lokasi donor."), - waktu_donor: yup - .string() - .required("Masukkan tanggal dan jam pelaksanaan donor.") - .test("datetime-is-in-the-right-format", "Masukkan waktu donor.", function( - value - ) { - return moment(new Date(value), "YYYY-MM-DD HH:mm", true).isValid() - }) + tanggal_donor: yup + .date() + .required("Masukkan tanggal pelaksanaan donor.") + .typeError("Masukkan tanggal yang valid.") + .min(moment().subtract(1, "days"), "Tanggal tidak boleh di waktu lampau."), + jam_mulai: yup + .date() + .required("Masukkan jam mulai pelaksanaan donor.") + .typeError("Masukkan waktu yang valid.") .test( - "time-exceeded-today", - "Tanggal acara donor harus melebihi hari ini.", + "is-3-hours-from-now", + "Jam mulai minimal harus lebih 3 jam dari sekarang.", function(value) { - return moment(new Date(value)).isAfter(moment(), "day") + const { tanggal_donor } = this.parent + const date = moment(tanggal_donor, "YYYY-MM-DD") + const time = moment(value, "HH:mm") + const dateTime = date.set({ + hour: time.get("hour"), + minute: time.get("minute"), + second: 0, + millisecond: 0, + }) + return dateTime.isSameOrAfter(moment().add(3, "hours")) + } + ), + jam_selesai: yup + .date() + .required("Masukkan jam berakhir pelaksanaan donor.") + .typeError("Masukkan waktu yang valid.") + .test( + "is-1-hour-after-start-time", + "Jam berakhir minimal harus lebih 1 jam dari jam mulai.", + function(value) { + const { jam_mulai } = this.parent + return moment(value, "HH:mm").isSameOrAfter( + moment(jam_mulai, "HH:mm").add(1, "hours") + ) } ), perkiraan_jumlah_donor: yup @@ -126,4 +151,16 @@ export const EventSubmissionSchema = yup.object().shape({ .positive("Masukkan perkiraan jumlah donor yang valid.") .moreThan(10, "Perkiraan jumlah pendonor harus lebih dari 10 orang."), keterangan: yup.string(), + foto_lokasi: yup + .mixed() + .test( + "is-in-the-right-size", + "Ukuran gambar terlalu besar. Ukuran gambar tidak boleh melebihi 50 MB.", + value => !value || value.size <= MAX_IMAGE_SIZE + ) + .test( + "is-in-the-right-format", + "Gambar harus dalam format jpg, jpeg, gif, atau png.", + value => !value || SUPPORTED_FORMATS.includes(value.type) + ), }) diff --git a/frontend/src/components/navbar.css b/frontend/src/components/navbar.css index 5caf34e92dbe71826dc707cdd60a11b24cf1c373..914f29446e00ea02158aec0531ec24c276acac1a 100644 --- a/frontend/src/components/navbar.css +++ b/frontend/src/components/navbar.css @@ -21,3 +21,13 @@ #change-profile-nav { margin-bottom: -0.1rem; } + +@media (max-width: 573px) { + .navbar-button { + display: block; + padding: 0; + margin-top: 0.5rem; + margin-bottom: 0.5rem; + text-align: left; + } +} diff --git a/frontend/src/components/navbar.js b/frontend/src/components/navbar.js index ebae17acd7be11795309c33b7ce71f75422976c9..5c8a540de42e22352c7909068bc92dbe210f34e5 100644 --- a/frontend/src/components/navbar.js +++ b/frontend/src/components/navbar.js @@ -25,6 +25,7 @@ const Navbar = () => { bg="red" variant="red" className="mb-4" + sticky="top" > @@ -32,19 +33,20 @@ const Navbar = () => { Home Jadwal Donor + Kumpulan Artikel {user && Profil} {user ? ( ) : (
+ )}
-
-
-
-
-
-
- Agenda Donor Darah + )} + + + + + + {isFetchingRiwayatAcaraDonor ? ( +
+
+ Riwayat Pengajuan Acara Donor Darah
- {statusAgenda === "loading" ? ( +
- ) : statusAgenda === "error" ? ( - - ) : ( -
- {!agendaDonor ? ( -
- Belum ada agenda donor. Yuk daftar di{" "} - sini -
- ) : ( - <> -

- Tanggal    :  - {moment(agendaDonor.time_start).format("D MMMM YYYY")} -

-

- Kecamatan   :  - {agendaDonor.kecamatan} -

-

- Lokasi      :  - {agendaDonor.location} -

-

- Jam      :  - {moment(agendaDonor.time_start).format("HH:mm") + - "-" + - moment(agendaDonor.time_end).format("HH:mm")} -

-

- Kuota      :  - {agendaDonor.quota} -

-

- Kategori    :  - {agendaDonor.category === "public" - ? "Umum" - : "Tertutup"} -

- - )} +
+
+ ) : statusRiwayatAcaraDonor === "error" ? ( +
+
+ Riwayat Pengajuan Acara Donor Darah +
+
+ +
+
+ ) : ( +
+ {dataRiwayatAcaraDonor.data.count === 0 ? ( +
+
+ Riwayat Pengajuan Acara Donor Darah +
+
+

+ Belum ada riwayat pengajuan acara donor darah. +

+
+ ) : ( + <> +
+ Riwayat Pengajuan Acara Donor Darah +
+ + + + + + + + + + {dataRiwayatAcaraDonor.data.results.map( + riwayatAcaraDonor => { + return ( + + + + + + ) + } + )} + +
AlamatWaktuStatus
+ {riwayatAcaraDonor.alamat_lokasi_donor} + + {moment(riwayatAcaraDonor.waktu_mulai).format( + "D MMMM YYYY," + )} +
+ {moment(riwayatAcaraDonor.waktu_mulai).format( + "(HH:mm " + )} + {moment( + riwayatAcaraDonor.waktu_berakhir + ).format("- HH:mm)")}{" "} +
+ {riwayatAcaraDonor.status == null ? ( +

Diajukan

+ ) : riwayatAcaraDonor.status ? ( +

Diterima

+ ) : ( +

Ditolak

+ )} +
+
    +
  • + pageRiwayatAcaraDonor > 1 && + setPageRiwayatAcaraDonor(curPage => curPage - 1) + } + > + + « + + Halaman sebelumnya riwayat acara donor + + +
  • + {paginationRiwayatAcaraDonor.map(val => ( +
  • setPageRiwayatAcaraDonor(val)} + key={val} + > + + {val} + + {`Halaman ${val} riwayat acara donor`} + + +
  • + ))} +
  • + pageRiwayatAcaraDonor < pageCountRiwayatAcaraDonor && + setPageRiwayatAcaraDonor(curPage => curPage + 1) + } + > + + » + + Halaman selanjutnya riwayat acara donor + + +
  • +
+ )}
-
-
-
-
-
-
-
-
-
-
-
-
-
- Riwayat Donor Darah -
- {isFetchingRiwayatDonor ? ( + )} + + + + {isFetchingRiwayatDonor ? ( +
+
Riwayat Donor Darah
+
- ) : statusRiwayatDonor === "error" ? ( +
+
+ ) : statusRiwayatDonor === "error" ? ( +
+
Riwayat Donor Darah
+
+
+
+ ) : ( +
+ {dataRiwayatDonor.data.count === 0 ? ( +
+
Riwayat Donor Darah
+
+

+ Belum ada riwayat donor darah. +

+
+
) : ( -
- {dataRiwayatDonor.data.count === 0 ? ( -
- Belum ada riwayat donor. -
- ) : ( - <> -
- - - - - - - - - {dataRiwayatDonor.data.results.map( - riwayatDonor => { - return ( - - - - - ) - } - )} - -
LokasiTanggal
{riwayatDonor.location} - {moment(riwayatDonor.time_start).format( - "D MMMM YYYY" - )} -
-
-
    -
  • - pageRiwayatDonor > 1 && - setPageRiwayatDonor(curPage => curPage - 1) - } - > - - « - - Halaman sebelumnya riwayat donor - - -
  • - {paginationRiwayatDonor.map(val => ( -
  • setPageRiwayatDonor(val)} - key={val} - > - - {val} - - {`Halaman ${val} riwayat donor`} - - -
  • - ))} -
  • - pageRiwayatDonor < pageCountRiwayatDonor && - setPageRiwayatDonor(curPage => curPage + 1) - } - > - - » - - Halaman selanjutnya riwayat donor - + <> +
    Riwayat Donor Darah
    + + + + + + + + + {dataRiwayatDonor.data.results.map(riwayatDonor => { + return ( + + + + + ) + })} + +
    LokasiTanggal
    + {riwayatDonor.location} + + {moment(riwayatDonor.time_start).format( + "D MMMM YYYY" + )} +
    +
      +
    • + pageRiwayatDonor > 1 && + setPageRiwayatDonor(curPage => curPage - 1) + } + > + + « + + Halaman sebelumnya riwayat donor + + +
    • + {paginationRiwayatDonor.map(val => ( +
    • setPageRiwayatDonor(val)} + key={val} + > + + {val} + + {`Halaman ${val} riwayat donor`} -
    • -
    - - )} -
+ + + ))} +
  • + pageRiwayatDonor < pageCountRiwayatDonor && + setPageRiwayatDonor(curPage => curPage + 1) + } + > + + » + + Halaman selanjutnya riwayat donor + + +
  • + + )}
    -
    -
    -
    + )} + +
    ) } -export default withAuthenticated(Profile) + +export default withAuthenticatedOrRedirect(Profile, "/") diff --git a/frontend/src/pages/profile.test.js b/frontend/src/pages/profile.test.js index ffd001392608b4ab7685bd83cbf36da052e94a9f..7f0b40d5db93380508e2ba0c8bee31688c2f8f5e 100644 --- a/frontend/src/pages/profile.test.js +++ b/frontend/src/pages/profile.test.js @@ -1,11 +1,19 @@ import { fireEvent, screen, waitFor } from "@testing-library/react" import React from "react" -import { getAgendaDonor, getRiwayatDonor, getUserProfile } from "../api" +import { + getAgendaDonor, + getRiwayatAcaraDonor, + getRiwayatDonor, + getUserProfile, + getFormulirDaftarDonor, +} from "../api" import { renderAuthenticated } from "../utils/test-util" +import { acaraDonorFactory } from "./acara-donor.factory" import { jadwalDonorFactory } from "./jadwal-donor.factory" import ProfilePage from "./profile" getAgendaDonor.mockResolvedValue({ data: [] }) getUserProfile.mockResolvedValue({ data: { profile: {} } }) +getRiwayatAcaraDonor.mockResolvedValue({ data: { count: 0, results: [] } }) getRiwayatDonor.mockResolvedValue({ data: { count: 0, results: [] } }) describe(`Kartu Donor`, () => { @@ -77,12 +85,12 @@ describe("Agenda Donor", () => { }) renderAuthenticated() await screen.findByText(/Beji/i) - expect(screen.getByText(/Beji/i)).toBeInTheDocument() - expect(screen.getByText(/Lokasi\s*:\s*D'Mall/i)).toBeInTheDocument() - expect(screen.getByText(/Kecamatan\s*:\s*Beji/i)).toBeInTheDocument() - expect(screen.getByText(/Tanggal\s*:\s*2 Maret 2020/i)).toBeInTheDocument() - expect(screen.getByText(/Kuota\s*:\s*150/i)).toBeInTheDocument() - expect(screen.getByText(/Kategori\s*:\s*Umum/i)).toBeInTheDocument() + expect(screen.getByText(/: Beji/i)).toBeInTheDocument() + expect(screen.getByText(/: D'Mall/i)).toBeInTheDocument() + expect(screen.getByText(/: Beji/i)).toBeInTheDocument() + expect(screen.getByText(/: 2 Maret 2020/i)).toBeInTheDocument() + expect(screen.getByText(/: 150/i)).toBeInTheDocument() + expect(screen.getByText(/: Umum/i)).toBeInTheDocument() await waitFor(() => expect(screen.queryByText("Loading...")).not.toBeInTheDocument() ) @@ -97,13 +105,61 @@ describe("Agenda Donor", () => { renderAuthenticated() expect(await screen.findByText(/Coba lagi/i)).toBeInTheDocument() fireEvent.click(screen.getByText(/Coba lagi/i)) - expect( - await screen.findByText(/Kategori\s*:\s*Tertutup/i) - ).toBeInTheDocument() + expect(await screen.findByText(/: Tertutup/i)).toBeInTheDocument() await waitFor(() => expect(screen.queryByText("Loading...")).not.toBeInTheDocument() ) }) + + it("can print formulir", async () => { + getAgendaDonor.mockResolvedValueOnce({ + data: [ + { + id: 1, + kecamatan: "Beji", + location: "D'Mall", + time_start: "2020-03-02T10:00:00+07:00", + time_end: "2020-03-02T15:00:00+07:00", + quota: 150, + category: "public", + }, + ], + }) + const printablePdfUrl = "https://printable.pdf" + getFormulirDaftarDonor.mockResolvedValueOnce({ + data: { + url: printablePdfUrl, + }, + }) + window.open = jest.fn() + renderAuthenticated() + fireEvent.click(await screen.findByText("Cetak Formulir")) + await waitFor(() => + expect(window.open).toHaveBeenCalledWith(printablePdfUrl, "_blank") + ) + }) + + it("can alert when print formulir fails", async () => { + getAgendaDonor.mockResolvedValueOnce({ + data: [ + { + id: 1, + kecamatan: "Beji", + location: "D'Mall", + time_start: "2020-03-02T10:00:00+07:00", + time_end: "2020-03-02T15:00:00+07:00", + quota: 150, + category: "public", + }, + ], + }) + const errorMessage = "Network error" + getFormulirDaftarDonor.mockRejectedValueOnce(new Error(errorMessage)) + window.alert = jest.fn() + renderAuthenticated() + fireEvent.click(await screen.findByText("Cetak Formulir")) + await waitFor(() => expect(window.alert).toHaveBeenCalledWith(errorMessage)) + }) }) describe("Riwayat Donor", () => { @@ -212,3 +268,146 @@ describe("Riwayat Donor", () => { ) }) }) + +describe("Riwayat Acara Donor", () => { + it("shows descriptive message when there is no riwayat acara donor", async () => { + getRiwayatAcaraDonor.mockResolvedValueOnce({ + data: { + count: 0, + results: [], + }, + }) + renderAuthenticated() + expect( + await screen.findByText(/Belum ada riwayat pengajuan acara donor darah./i) + ).toBeInTheDocument() + await waitFor(() => + expect(screen.queryByText("Loading...")).not.toBeInTheDocument() + ) + }) + + it("shows riwayat acara donor on success", async () => { + getRiwayatAcaraDonor.mockResolvedValue({ + data: { + count: 1, + results: [ + { + id: 1, + status: false, + alamat_institusi: "Pacilkom", + alamat_lokasi_donor: "Sekre Pacil", + email_kantor: "pacil@cs.ui.ac.id", + email_koor: "pacilia@gmail.com", + kategori: "Terbuka", + kecamatan: "Beji", + keterangan: "", + nama_institusi: "Pacilkom", + nama_koor: "dr. Pacilia", + no_telp_kantor: "08165342342", + no_telp_koor: "08167021743", + perkiraan_jumlah_donor: 455, + waktu_berakhir: "2020-06-02T12:00:00+07:00", + waktu_mulai: "2020-06-02T10:00:00+07:00", + }, + ], + }, + }) + renderAuthenticated() + await screen.findByText("Alamat") + expect(screen.getByText("Sekre Pacil")) + expect(screen.getByText("Ditolak")) + await waitFor(() => + expect(screen.queryByText("Loading...")).not.toBeInTheDocument() + ) + }) + + it("shows correct status for acara donor", async () => { + const acara_donor1 = acaraDonorFactory({ + status: null, + }) + const acara_donor2 = acaraDonorFactory({ + status: false, + }) + const acara_donor3 = acaraDonorFactory({ + status: true, + }) + getRiwayatAcaraDonor.mockResolvedValueOnce({ + data: { + count: 3, + results: [acara_donor1, acara_donor2, acara_donor3], + }, + }) + renderAuthenticated() + await screen.findByText("Alamat") + expect(screen.getByText("Diajukan")).toBeInTheDocument() + expect(screen.getByText("Ditolak")).toBeInTheDocument() + expect(screen.getByText("Diterima")).toBeInTheDocument() + }) + + it("has clickable pagination", async () => { + const acara_donor1 = acaraDonorFactory({ + alamat_lokasi_donor: "Yuli Pacil", + }) + const acara_donor4 = acaraDonorFactory({ + alamat_lokasi_donor: "Belyos Pacil", + }) + getRiwayatAcaraDonor + .mockResolvedValueOnce({ + data: { + count: 4, + results: [acara_donor1, acaraDonorFactory(), acaraDonorFactory()], + }, + }) + .mockResolvedValueOnce({ + data: { + count: 4, + results: [acara_donor4], + }, + }) + .mockResolvedValueOnce({ + data: { + count: 4, + results: [acara_donor1, acaraDonorFactory(), acaraDonorFactory()], + }, + }) + .mockResolvedValueOnce({ + data: { + count: 4, + results: [acara_donor4], + }, + }) + renderAuthenticated() + expect(await screen.findByText("Yuli Pacil")).toBeInTheDocument() + fireEvent.click(screen.getByText("Halaman 2 riwayat acara donor")) + expect(await screen.findByText("Belyos Pacil")).toBeInTheDocument() + fireEvent.click(screen.getByText("Halaman sebelumnya riwayat acara donor")) + expect(await screen.findByText("Yuli Pacil")).toBeInTheDocument() + fireEvent.click(screen.getByText("Halaman selanjutnya riwayat acara donor")) + expect(await screen.findByText("Belyos Pacil")).toBeInTheDocument() + await waitFor(() => + expect(screen.queryByText("Loading...")).not.toBeInTheDocument() + ) + }) + + it("shows retry button on error and can be retried", async () => { + getRiwayatAcaraDonor + .mockRejectedValueOnce(new Error("Network error")) + .mockResolvedValueOnce({ + data: { + count: 1, + results: [ + acaraDonorFactory({ + alamat_lokasi_donor: "Yuli Pacil", + }), + ], + }, + }) + renderAuthenticated() + expect(await screen.findByText(/Coba lagi/i)).toBeInTheDocument() + fireEvent.click(screen.getByText(/Coba lagi/i)) + expect(await screen.findByText("Yuli Pacil")).toBeInTheDocument() + await waitFor(() => + expect(screen.queryByText("Loading...")).not.toBeInTheDocument() + ) + }) +}) diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css index d44c73034cb1af4b484d9bdc8e799f16da8f4aad..bd84db4bd9576df6043e684285471bfc4382a600 100644 --- a/frontend/src/styles/global.css +++ b/frontend/src/styles/global.css @@ -20,16 +20,25 @@ body { background-color: var(--cream); } +.MuiOutlinedInput-input { + padding: 10px 14px !important; +} +.MuiInputBase-input:hover { + text-shadow: 0.7px 0 0 currentColor; +} +.MuiInputBase-input:focus { + text-shadow: 0.7px 0 0 currentColor; +} + .form-control { color: var(--blue); background-color: var(--white); border-color: var(--red) !important; - box-shadow: 0 0 2px 1px var(--red) !important; } .form-control:hover { color: var(--blue); - text-shadow: 0 0 0.65px var(--blue), 0 0 0.65px var(--blue); + text-shadow: 0.7px 0 0 currentColor; background-color: var(--white); border-color: var(--dark-red) !important; box-shadow: 0 0 2px 1px var(--dark-red) !important; @@ -37,12 +46,16 @@ body { .form-control:focus { color: var(--blue); - text-shadow: 0 0 0.65px var(--blue), 0 0 0.65px var(--blue); + text-shadow: 0.7px 0 0 currentColor; background-color: var(--white); border-color: var(--dark-red) !important; box-shadow: 4px 4px var(--dark-red) !important; } +input { + color: var(--blue) !important; +} + .text-red { color: var(--red); } @@ -193,6 +206,17 @@ body { box-shadow: var(--bottom-shadow); } +.tooltip { + font-weight: bold; +} +.arrow::before { + border-right-color: var(--red) !important; +} +.tooltip-inner { + color: var(--cream); + background-color: var(--red); +} + @media (max-width: 1024px) { .form-control { font-size: 15px; @@ -234,6 +258,21 @@ body { content: "⚠ "; } +.thumbnail { + position: relative; +} + +.caption { + position: absolute; + top: 75%; + left: 0; + width: 100%; +} + #title { text-align: center; } + +.unstyled-link:hover { + text-decoration: none; +} diff --git a/frontend/src/styles/theme.js b/frontend/src/styles/theme.js new file mode 100644 index 0000000000000000000000000000000000000000..8f233759e2134d9ceb99aa0c2a452269948e41fe --- /dev/null +++ b/frontend/src/styles/theme.js @@ -0,0 +1,75 @@ +import { createMuiTheme } from "@material-ui/core/styles" + +const RED = "#a91320" +const DARK_RED = "#8a1324" +const BLUE = "#376c8b" +const DARK_BLUE = "#285e9b" +const CREAM = "#fff3df" +const DARK_CREAM = "#f9e5cc" +const ERROR_RED = "#ff0000" + +const theme = createMuiTheme({ + palette: { + primary: { + main: RED, + dark: DARK_RED, + contrastText: CREAM, + }, + secondary: { + main: BLUE, + dark: DARK_BLUE, + contrastText: CREAM, + }, + error: { + main: ERROR_RED, + }, + }, + overrides: { + MuiIconButton: { + root: { + color: BLUE, + }, + }, + MuiOutlinedInput: { + root: { + position: "relative", + "& $notchedOutline": { + borderColor: RED, + }, + "&:hover:not($disabled):not($focused):not($error) $notchedOutline": { + borderColor: DARK_RED, + boxShadow: "0 0 2px 1px #8a1324 !important", + // Reset on touch devices, it doesn't add specificity + "#media (hover: none)": { + borderColor: DARK_RED, + boxShadow: "0 0 2px 1px #8a1324 !important", + }, + }, + "&$focused $notchedOutline": { + borderColor: DARK_RED, + boxShadow: "4px 4px #8a1324 !important", + }, + }, + }, + MuiPickersBasePicker: { + pickerView: { + backgroundColor: CREAM, + }, + pickerViewLandscape: { + backgroundColor: CREAM, + }, + }, + MuiPickersCalendarHeader: { + iconButton: { + backgroundColor: DARK_CREAM, + }, + }, + MuiPickersClock: { + clock: { + backgroundColor: DARK_CREAM, + }, + }, + }, +}) + +export default theme diff --git a/frontend/src/utils/test-util.js b/frontend/src/utils/test-util.js index cf602b4ea210bc21941e43ab708c0782a60c4748..7e6319ba22d3774d45919601d21eaeb9dca9a626 100644 --- a/frontend/src/utils/test-util.js +++ b/frontend/src/utils/test-util.js @@ -1,3 +1,8 @@ +import { + createHistory, + createMemorySource, + LocationProvider, +} from "@reach/router" import { render as renderTestingLibrary } from "@testing-library/react" import React from "react" import { AuthProvider } from "../hooks/authenticate" @@ -10,3 +15,13 @@ export const renderAuthenticated = component => renderTestingLibrary( {component} ) + +export const renderWithRouter = ( + ui, + { route = "/", history = createHistory(createMemorySource(route)) } = {} +) => { + return { + ...render({ui}), + history, + } +} diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 8870c541da68da352a3cf479e7b3a7aa7c33420a..e73b20907ae4e43d4f68fdb0212fb0f308c718db 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -21,7 +21,7 @@ http { server { listen 8000 default_server; listen [::]:8000 default_server; - server_name localhost; + server_name ppl2020c6.cs.ui.ac.id; charset utf-8; root /var/www/html; index index.html; @@ -29,10 +29,16 @@ http { location /api/static { try_files $uri $uri/ $uri/index.html; } + + location /api/media { + } location /api { proxy_pass http://api; proxy_set_header SCRIPT_NAME /api; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; } location / { diff --git a/nginx/nginx.dev.conf b/nginx/nginx.dev.conf index 4eee5985ad1ab59e95dddbf77f9aa6680bc24eca..38c415d0e9aae31010cfc405b6e4232e00fcafba 100644 --- a/nginx/nginx.dev.conf +++ b/nginx/nginx.dev.conf @@ -21,7 +21,7 @@ http { server { listen 8000 default_server; listen [::]:8000 default_server; - server_name localhost; + server_name ppl2020c6.cs.ui.ac.id; charset utf-8; root /var/www/html; index index.html; @@ -30,9 +30,15 @@ http { try_files $uri $uri/ $uri/index.html; } + location /staging/api/media { + } + location /staging/api { proxy_pass http://api; proxy_set_header SCRIPT_NAME /staging/api; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; } location /staging {