diff --git a/opentech/apply/dashboard/templates/dashboard/dashboard.html b/opentech/apply/dashboard/templates/dashboard/dashboard.html index d0a54dca98910e400df9fb6b68446f6aa750577f..535b01f95de65c62eec5e79c42c293ed9778c8d8 100644 --- a/opentech/apply/dashboard/templates/dashboard/dashboard.html +++ b/opentech/apply/dashboard/templates/dashboard/dashboard.html @@ -2,6 +2,10 @@ {% load render_table from django_tables2 %} {% load static %} +{% block extra_css %} + {{ filter.form.media.css }} +{% endblock %} + {% block title %}Dashboard{% endblock %} {% block content %} @@ -17,23 +21,44 @@ </div> </div> <div class="wrapper wrapper--large wrapper--inner-space-medium"> - <div class="wrapper wrapper--large wrapper--inner-space-medium"> + + <div> + + {% include "dashboard/includes/waiting-for-review.html" with in_review_count=in_review_count my_review=my_review display_more=display_more %} + {% if closed_rounds or open_rounds %} {% include "funds/includes/round-block.html" with closed_rounds=closed_rounds open_rounds=open_rounds title=rounds_title %} {% endif %} - <h3>Applications to review</h3> - {% if in_review.data %} - {% render_table in_review %} - {% else %} - No reviews to complete + </div> + + <div> + {% if my_reviewed.data %} + + <h4 class="heading heading--normal"> + Your previous reviews + </h4> + + {% include "funds/includes/table_filter_and_search.html" with filter_form=filter_form search_term=search_term use_search=True %} + {% render_table my_reviewed %} + + {% if display_more_reviewed %} + <div class="all-submissions-table__more"> + <a href="{% url 'apply:submissions:list' %}?reviewers={{ request.user.pk }}">Show all</a> + </div> + {% endif %} + {% endif %} </div> + </div> {% endblock %} {% block extra_js %} + {{ filter.form.media.js }} + <script src="https://cdnjs.cloudflare.com/ajax/libs/url-search-params/1.1.0/url-search-params.js"></script> + <script src="{% static 'js/apply/submission-filters.js' %}"></script> <script src="{% static 'js/apply/submission-tooltips.js' %}"></script> <script src="{% static 'js/apply/tabs.js' %}"></script> {% endblock %} diff --git a/opentech/apply/dashboard/templates/dashboard/includes/waiting-for-review.html b/opentech/apply/dashboard/templates/dashboard/includes/waiting-for-review.html new file mode 100644 index 0000000000000000000000000000000000000000..2caf8fb815c2ca670ab3f1a8e652163105d7d215 --- /dev/null +++ b/opentech/apply/dashboard/templates/dashboard/includes/waiting-for-review.html @@ -0,0 +1,24 @@ +{% load render_table from django_tables2 %} + +<h4 class="heading heading--normal"> + Waiting for your review <span class="heading heading--submission-count">{{ in_review_count }}</span> +</h4> + +{% if my_review.data %} + {% render_table my_review %} + + {% if display_more %} + <div class="all-submissions-table__more"> + <a href="{% url 'apply:submissions:list' %}?reviewers={{ request.user.pk }}">Show all</a> + </div> + {% endif %} + +{% else %} + <div class="reviewer-dash-box"> + <h5 class="reviewer-dash-box__title">Nice! You're all caught up.</h5> + <a class="button button--primary" href="{% url 'apply:submissions:list' %}">Find new applications to review</a> + </div> + {# TODO Fill in data and update styles in future ticket #} + <div>{# Since you last logged in #}</div> + +{% endif %} \ No newline at end of file diff --git a/opentech/apply/dashboard/templates/dashboard/reviewer_dashboard.html b/opentech/apply/dashboard/templates/dashboard/reviewer_dashboard.html index fbfe0d1ff7c313a46d409a0eb8b2cfb923c5b6b1..cbbbce4ea416cb9a6d8a0ec44defea4ac28ec9c0 100644 --- a/opentech/apply/dashboard/templates/dashboard/reviewer_dashboard.html +++ b/opentech/apply/dashboard/templates/dashboard/reviewer_dashboard.html @@ -2,6 +2,10 @@ {% load render_table from django_tables2 %} {% load static %} +{% block extra_css %} + {{ filter.form.media.css }} +{% endblock %} + {% block title %}OTF reviewer Dashboard{% endblock %} {% block content %} @@ -12,31 +16,41 @@ {% endblock %} </div> </div> + <div class="wrapper wrapper--large wrapper--inner-space-medium"> - <div class="wrapper wrapper--large wrapper--inner-space-medium"> - <h3>Applications to review</h3> - {% if my_review.data %} - <p>These are applications for which your review has been requested.</p> - {% render_table my_review %} - {% else %} - No reviews to complete + + <div> + + {% include "dashboard/includes/waiting-for-review.html" with in_review_count=in_review_count my_review=my_review display_more=display_more %} + + </div> + + <div> + {% if my_reviewed.data %} + + <h4 class="heading heading--normal"> + Your previous reviews + </h4> + + {% include "funds/includes/table_filter_and_search.html" with filter_form=filter_form search_term=search_term use_search=True %} + {% render_table my_reviewed %} + + {% if display_more_reviewed %} + <div class="all-submissions-table__more"> + <a href="{% url 'apply:submissions:list' %}">Show all</a> + </div> {% endif %} - </div> -</div> -<div class="wrapper wrapper--large wrapper--inner-space-medium"> - <div class="wrapper wrapper--large wrapper--inner-space-medium"> - <h3>Other Applications in review</h3> - {% if also_in_review.data %} - <p>You are not assigned to review these applications but they may be of interest</p> - {% render_table also_in_review %} - {% else %} - There are no other applications in review {% endif %} </div> </div> + {% endblock %} {% block extra_js %} + {{ filter.form.media.js }} + <script src="https://cdnjs.cloudflare.com/ajax/libs/url-search-params/1.1.0/url-search-params.js"></script> + <script src="{% static 'js/apply/submission-filters.js' %}"></script> <script src="{% static 'js/apply/submission-tooltips.js' %}"></script> {% endblock %} + diff --git a/opentech/apply/dashboard/tests/test_views.py b/opentech/apply/dashboard/tests/test_views.py index 795fcf11f404596c3f6e6eeb885cfef8a8fc3a87..dcbf42935261c82df055e32c2508203d33604843 100644 --- a/opentech/apply/dashboard/tests/test_views.py +++ b/opentech/apply/dashboard/tests/test_views.py @@ -3,7 +3,7 @@ from opentech.apply.funds.tests.factories import ( ApplicationRevisionFactory, InvitedToProposalFactory, ) -from opentech.apply.users.tests.factories import UserFactory, StaffFactory +from opentech.apply.users.tests.factories import UserFactory, ReviewerFactory, StaffFactory from opentech.apply.utils.testing.tests import BaseViewTestCase @@ -61,3 +61,35 @@ class TestStaffDashboard(BaseViewTestCase): ApplicationSubmissionFactory(status='concept_review_discussion', workflow_stages=2, form_data__title='Reviewr') response = self.get_page() self.assertNotContains(response, 'Reviewr') + + def test_waiting_for_review_with_count(self): + submission = ApplicationSubmissionFactory(status='external_review', workflow_stages=2, reviewers=[self.user]) + response = self.get_page() + self.assertContains(response, 'Waiting for your review') + self.assertContains(response, submission.title) + self.assertEquals(response.context['in_review_count'], 1) + + +class TestReviewerDashboard(BaseViewTestCase): + user_factory = ReviewerFactory + url_name = 'dashboard:{}' + base_view_name = 'dashboard' + + def test_waiting_for_review_with_count(self): + submission = ApplicationSubmissionFactory(status='external_review', workflow_stages=2, reviewers=[self.user]) + response = self.get_page() + self.assertContains(response, 'Waiting for your review') + self.assertContains(response, submission.title) + self.assertEquals(response.context['in_review_count'], 1) + + def test_no_submissions_waiting_for_review(self): + submission = ApplicationSubmissionFactory(status='external_review', workflow_stages=2, reviewers=[]) + response = self.get_page() + self.assertNotContains(response, submission.title) + self.assertEquals(response.context['in_review_count'], 0) + + def test_submission_assigned_but_not_in_external_review_status(self): + submission = ApplicationSubmissionFactory(status='concept_review_discussion', workflow_stages=2, reviewers=[self.user]) + response = self.get_page() + self.assertNotContains(response, submission.title) + self.assertEquals(response.context['in_review_count'], 0) diff --git a/opentech/apply/dashboard/views.py b/opentech/apply/dashboard/views.py index 22cba795cec3e3440864a9f85a9cc269ba1656ad..cd83dc6828e37c53ee90544e77d2d322a3c9d056 100644 --- a/opentech/apply/dashboard/views.py +++ b/opentech/apply/dashboard/views.py @@ -1,20 +1,32 @@ +from django.http import HttpResponseRedirect from django.shortcuts import render from django.views.generic import TemplateView -from django_tables2 import RequestConfig +from django.urls import reverse_lazy from django_tables2.views import SingleTableView from opentech.apply.funds.models import ApplicationSubmission, RoundsAndLabs -from opentech.apply.funds.tables import SubmissionsTable, AdminSubmissionsTable +from opentech.apply.funds.tables import ( + ReviewerSubmissionsTable, + SubmissionFilterAndSearch, + SubmissionReviewerFilterAndSearch, + SubmissionsTable, + SummarySubmissionsTable, +) from opentech.apply.utils.views import ViewDispatcher class AdminDashboardView(TemplateView): def get(self, request, *args, **kwargs): + # redirect to submissions list when we use the filter to search for something + if len(request.GET): + query_str = '?' + for key, value in request.GET.items(): + query_str += key + '=' + value + '&' + return HttpResponseRedirect(reverse_lazy('funds:submissions:list') + query_str) + qs = ApplicationSubmission.objects.all().for_table(self.request.user) - in_review = SubmissionsTable(qs.in_review_for(request.user), prefix='in-review-') - RequestConfig(request, paginate={'per_page': 10}).configure(in_review) base_query = RoundsAndLabs.objects.with_progress().active().order_by('-end_date') base_query = base_query.by_lead(request.user) open_rounds = base_query.open()[:6] @@ -23,34 +35,111 @@ class AdminDashboardView(TemplateView): closed_query = '?round_state=closed' rounds_title = 'Your rounds and labs' + # Staff reviewer's current to-review submissions + my_review_qs, my_review, display_more = self.get_my_reviews(request.user, qs) + + # Staff reviewer's reviewed submissions for 'Previous reviews' block + filterset, my_reviewed_qs, my_reviewed, display_more_reviewed = self.get_my_reviewed(request, qs) + return render(request, 'dashboard/dashboard.html', { - 'in_review': in_review, 'open_rounds': open_rounds, 'open_query': open_query, 'closed_rounds': closed_rounds, 'closed_query': closed_query, 'rounds_title': rounds_title, + 'my_review': my_review, + 'in_review_count': my_review_qs.count(), + 'display_more': display_more, + 'my_reviewed': my_reviewed, + 'display_more_reviewed': display_more_reviewed, + 'filter': filterset, }) + def get_my_reviews(self, user, qs): + my_review_qs = qs.in_review_for(user).order_by('-submit_time') + my_review_table = SummarySubmissionsTable(my_review_qs[:5], prefix='my-review-') + display_more = (my_review_qs.count() > 5) + + return my_review_qs, my_review_table, display_more + + def get_my_reviewed(self, request, qs): + # Replicating django_filters.views.FilterView + my_reviewed_qs = qs.reviewed_by(request.user).order_by('-submit_time') + kwargs = { + 'data': self.request.GET or None, + 'request': self.request, + 'queryset': my_reviewed_qs, + } + filterset = SubmissionFilterAndSearch(**kwargs) + my_reviewed_qs = filterset.qs + + my_reviewed_table = SummarySubmissionsTable(my_reviewed_qs[:5], prefix='my-reviewed-') + display_more_reviewed = (my_reviewed_qs.count() > 5) + + return filterset, my_reviewed_qs, my_reviewed_table, display_more_reviewed + class ReviewerDashboardView(TemplateView): + def get(self, request, *args, **kwargs): + # redirect to submissions list when we use the filter to search for something + if len(request.GET): + query_str = '?' + for key, value in request.GET.items(): + query_str += key + '=' + value + '&' + return HttpResponseRedirect(reverse_lazy('funds:submissions:list') + query_str) + qs = ApplicationSubmission.objects.all().for_table(self.request.user) - my_review_qs = qs.in_review_for(request.user) - my_review = SubmissionsTable(my_review_qs, prefix='my-review-') - RequestConfig(request, paginate={'per_page': 10}).configure(my_review) + # Reviewer's current to-review submissions + my_review_qs, my_review, display_more = self.get_my_reviews(request.user, qs) - also_in_review = AdminSubmissionsTable( - qs.in_review_for(request.user, assigned=False).exclude(id__in=my_review_qs), - prefix='also-in-review-' - ) - RequestConfig(request, paginate={'per_page': 10}).configure(also_in_review) + # Reviewer's reviewed submissions and filters for 'Previous reviews' block + filterset, my_reviewed_qs, my_reviewed, display_more_reviewed = self.get_my_reviewed(request, qs) - return render(request, 'dashboard/reviewer_dashboard.html', { + context = { 'my_review': my_review, - 'also_in_review': also_in_review, - }) + 'in_review_count': my_review_qs.count(), + 'display_more': display_more, + 'my_reviewed': my_reviewed, + 'display_more_reviewed': display_more_reviewed, + 'filter': filterset, + } + + return render(request, 'dashboard/reviewer_dashboard.html', context) + + def get_my_reviews(self, user, qs): + my_review_qs = qs.in_review_for(user).order_by('-submit_time') + my_review_table = ReviewerSubmissionsTable(my_review_qs[:5], prefix='my-review-') + display_more = (my_review_qs.count() > 5) + + return my_review_qs, my_review_table, display_more + + def get_my_reviewed(self, request, qs): + # Replicating django_filters.views.FilterView + my_reviewed_qs = qs.reviewed_by(request.user).order_by('-submit_time') + + kwargs = { + 'data': request.GET or None, + 'request': request, + 'queryset': my_reviewed_qs, + } + filterset = SubmissionReviewerFilterAndSearch(**kwargs) + my_reviewed_qs = filterset.qs + + my_reviewed_table = ReviewerSubmissionsTable(my_reviewed_qs[:5], prefix='my-reviewed-') + display_more_reviewed = (my_reviewed_qs.count() > 5) + + return filterset, my_reviewed_qs, my_reviewed_table, display_more_reviewed + + def get_context_data(self, **kwargs): + kwargs = super().get_context_data(**kwargs) + search_term = self.request.GET.get('query') + kwargs.update( + search_term=search_term, + ) + + return super().get_context_data(**kwargs) class ApplicantDashboardView(SingleTableView): diff --git a/opentech/apply/funds/models/submissions.py b/opentech/apply/funds/models/submissions.py index 0adeb684360ff6ddfa38b4406b7b2d01e752ea1f..397b2ec9f532e500785eeb5ea459277673369b45 100644 --- a/opentech/apply/funds/models/submissions.py +++ b/opentech/apply/funds/models/submissions.py @@ -85,7 +85,10 @@ class ApplicationSubmissionQueryset(JSONOrderable): qs = self.filter(Q(status__in=user_review_statuses), ~Q(reviews__author=user) | Q(reviews__is_draft=True)) if assigned: qs = qs.filter(reviewers=user) - return qs + return qs.distinct() + + def reviewed_by(self, user): + return self.filter(reviews__author=user) def awaiting_determination_for(self, user): return self.filter(status__in=DETERMINATION_RESPONSE_PHASES).filter(lead=user) diff --git a/opentech/apply/funds/tables.py b/opentech/apply/funds/tables.py index b49943f90a7c98996142e0df56f073873d95516e..620040584ec9b00af6ec9de12566ffe2ba00d095 100644 --- a/opentech/apply/funds/tables.py +++ b/opentech/apply/funds/tables.py @@ -65,6 +65,11 @@ class SubmissionsTable(tables.Table): return qs, True +class ReviewerSubmissionsTable(SubmissionsTable): + class Meta(SubmissionsTable.Meta): + orderable = False + + class LabeledCheckboxColumn(tables.CheckBoxColumn): def wrap_with_label(self, checkbox, for_value): return format_html( @@ -203,6 +208,28 @@ class SubmissionFilterAndSearch(SubmissionFilter): query = filters.CharFilter(field_name='search_data', lookup_expr="icontains", widget=forms.HiddenInput) +class SubmissionDashboardFilter(filters.FilterSet): + round = Select2ModelMultipleChoiceFilter(queryset=get_used_rounds, label='Rounds') + fund = Select2ModelMultipleChoiceFilter(name='page', queryset=get_used_funds, label='Funds') + + class Meta: + model = ApplicationSubmission + fields = ('fund', 'round') + + def __init__(self, *args, exclude=list(), limit_statuses=None, **kwargs): + super().__init__(*args, **kwargs) + + self.filters = { + field: filter + for field, filter in self.filters.items() + if field not in exclude + } + + +class SubmissionReviewerFilterAndSearch(SubmissionDashboardFilter): + query = filters.CharFilter(field_name='search_data', lookup_expr="icontains", widget=forms.HiddenInput) + + class RoundsTable(tables.Table): title = tables.LinkColumn('funds:rounds:detail', args=[A('pk')], orderable=True, text=lambda record: record.title) fund = tables.Column(accessor=A('specific.fund')) diff --git a/opentech/apply/funds/views.py b/opentech/apply/funds/views.py index 67d504a0ff2eb369940586ed130e7571d769bc8f..44145ee6f97929c79886b7a4f27bdb12ee400ba3 100644 --- a/opentech/apply/funds/views.py +++ b/opentech/apply/funds/views.py @@ -41,15 +41,16 @@ from .forms import ( from .models import ApplicationSubmission, ApplicationRevision, RoundsAndLabs, RoundBase, LabBase from .tables import ( AdminSubmissionsTable, + ReviewerSubmissionsTable, RoundsTable, RoundsFilter, SubmissionFilterAndSearch, + SubmissionReviewerFilterAndSearch, SummarySubmissionsTable, ) from .workflow import STAGE_CHANGE_ACTIONS, PHASES_MAPPING -@method_decorator(staff_required, name='dispatch') class BaseAdminSubmissionsTable(SingleTableMixin, FilterView): table_class = AdminSubmissionsTable filterset_class = SubmissionFilterAndSearch @@ -120,6 +121,16 @@ class BatchUpdateReviewersView(DelegatedViewMixin, FormView): return super().form_valid(form) +class BaseReviewerSubmissionsTable(BaseAdminSubmissionsTable): + table_class = ReviewerSubmissionsTable + filterset_class = SubmissionReviewerFilterAndSearch + + def get_queryset(self): + # Reviewers can only see submissions they have reviewed + return super().get_queryset().reviewed_by(self.request.user) + + +@method_decorator(staff_required, name='dispatch') class SubmissionOverviewView(AllActivityContextMixin, BaseAdminSubmissionsTable): template_name = 'funds/submissions_overview.html' table_class = SummarySubmissionsTable @@ -147,13 +158,23 @@ class SubmissionOverviewView(AllActivityContextMixin, BaseAdminSubmissionsTable) ) -class SubmissionListView(AllActivityContextMixin, BaseAdminSubmissionsTable, DelegateableListView): +class SubmissionAdminListView(AllActivityContextMixin, BaseAdminSubmissionsTable, DelegateableListView): template_name = 'funds/submissions.html' form_views = [ BatchUpdateReviewersView ] +class SubmissionReviewerListView(AllActivityContextMixin, BaseReviewerSubmissionsTable): + template_name = 'funds/submissions.html' + + +class SubmissionListView(ViewDispatcher): + admin_view = SubmissionAdminListView + reviewer_view = SubmissionReviewerListView + + +@method_decorator(staff_required, name='dispatch') class SubmissionsByRound(AllActivityContextMixin, BaseAdminSubmissionsTable, DelegateableListView): template_name = 'funds/submissions_by_round.html' form_views = [ @@ -177,6 +198,7 @@ class SubmissionsByRound(AllActivityContextMixin, BaseAdminSubmissionsTable, Del return super().get_context_data(object=self.obj, **kwargs) +@method_decorator(staff_required, name='dispatch') class SubmissionsByStatus(BaseAdminSubmissionsTable): template_name = 'funds/submissions_by_status.html' status_mapping = PHASES_MAPPING diff --git a/opentech/static_src/src/sass/apply/components/_heading.scss b/opentech/static_src/src/sass/apply/components/_heading.scss index a341b61a95be697daa8b483e7db97d89df4abc17..d6fa51adb55523256f69d3ec775dbb7feeda6cbe 100644 --- a/opentech/static_src/src/sass/apply/components/_heading.scss +++ b/opentech/static_src/src/sass/apply/components/_heading.scss @@ -75,4 +75,17 @@ text-align: left; } } + + &--submission-count { + display: inline-block; + padding: 2px 10px; + font-size: 13px; + font-weight: $weight--bold; + color: $color--marine; + text-align: center; + background-color: $color--sky-blue; + border-radius: 20%; + border: 1px solid $color--light-blue-90; + vertical-align: middle; + } } diff --git a/opentech/static_src/src/sass/apply/components/_reviewer-dash-box.scss b/opentech/static_src/src/sass/apply/components/_reviewer-dash-box.scss new file mode 100644 index 0000000000000000000000000000000000000000..08ea5888bf4600fb1408b0fe1143fdcf83f0b281 --- /dev/null +++ b/opentech/static_src/src/sass/apply/components/_reviewer-dash-box.scss @@ -0,0 +1,11 @@ +.reviewer-dash-box { + display: flex; + align-items: center; + flex-direction: column; + padding: 30px; + border: 1px solid $color--mid-grey; + + &__title { + color: $color--mid-dark-grey; + } +} diff --git a/opentech/static_src/src/sass/apply/main.scss b/opentech/static_src/src/sass/apply/main.scss index 386c5f11111e127b697b8f9782045cb4176366b7..5641137b682abc9531742424676a0f6eda99c3d5 100644 --- a/opentech/static_src/src/sass/apply/main.scss +++ b/opentech/static_src/src/sass/apply/main.scss @@ -36,6 +36,7 @@ @import 'components/nav'; @import 'components/pagination'; @import 'components/profile'; +@import 'components/reviewer-dash-box'; @import 'components/reviews-list'; @import 'components/reviews-summary'; @import 'components/reviews-sidebar'; diff --git a/package-lock.json b/package-lock.json index 19268ff324e2a31f659e208d18d2d138b7e91404..d10f0471c2081a3079170793b5d8651760f5bb49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1523,7 +1523,7 @@ }, "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, @@ -2460,9 +2460,9 @@ } }, "connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", "dev": true }, "console-browserify": { @@ -2487,7 +2487,7 @@ }, "content-disposition": { "version": "0.5.2", - "resolved": "http://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", "dev": true }, @@ -3861,7 +3861,7 @@ "dependencies": { "array-flatten": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, @@ -4096,7 +4096,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -4243,9 +4243,9 @@ } }, "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.6.1.tgz", + "integrity": "sha512-t2JCjbzxQpWvbhts3l6SH1DKzSrx8a+SsaVf4h6bG4kOXUuPYS/kg2Lr4gQSb7eemaHqJkOThF1BGyjlUkO1GQ==", "dev": true, "requires": { "debug": "=3.1.0" @@ -5583,9 +5583,9 @@ } }, "handle-thing": { - "version": "1.2.5", - "resolved": "http://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", - "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", "dev": true }, "har-schema": { @@ -5774,7 +5774,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -5803,7 +5803,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -6966,7 +6966,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -9950,65 +9950,62 @@ "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==" }, "spdy": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", - "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", + "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", "dev": true, "requires": { - "debug": "^2.6.8", - "handle-thing": "^1.2.5", + "debug": "^4.1.0", + "handle-thing": "^2.0.0", "http-deceiver": "^1.2.7", - "safe-buffer": "^5.0.1", "select-hose": "^2.0.0", - "spdy-transport": "^2.0.18" + "spdy-transport": "^3.0.0" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true } } }, "spdy-transport": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.1.tgz", - "integrity": "sha512-q7D8c148escoB3Z7ySCASadkegMmUZW8Wb/Q1u0/XBgDKMO880rLQDj8Twiew/tYi7ghemKUi/whSYOwE17f5Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, "requires": { - "debug": "^2.6.8", - "detect-node": "^2.0.3", + "debug": "^4.1.0", + "detect-node": "^2.0.4", "hpack.js": "^2.1.6", - "obuf": "^1.1.1", - "readable-stream": "^2.2.9", - "safe-buffer": "^5.0.1", - "wbuf": "^1.7.2" + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "readable-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", + "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } } } }, @@ -11443,9 +11440,9 @@ } }, "webpack-dev-server": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.10.tgz", - "integrity": "sha512-RqOAVjfqZJtQcB0LmrzJ5y4Jp78lv9CK0MZ1YJDTaTmedMZ9PU9FLMQNrMCfVu8hHzaVLVOJKBlGEHMN10z+ww==", + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz", + "integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -11467,12 +11464,14 @@ "portfinder": "^1.0.9", "schema-utils": "^1.0.0", "selfsigned": "^1.9.1", + "semver": "^5.6.0", "serve-index": "^1.7.2", "sockjs": "0.3.19", "sockjs-client": "1.3.0", - "spdy": "^3.4.1", + "spdy": "^4.0.0", "strip-ansi": "^3.0.0", "supports-color": "^5.1.0", + "url": "^0.11.0", "webpack-dev-middleware": "3.4.0", "webpack-log": "^2.0.0", "yargs": "12.0.2" @@ -11512,6 +11511,19 @@ } } }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, "decamelize": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", @@ -11521,6 +11533,21 @@ "xregexp": "4.0.0" } }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -11530,6 +11557,15 @@ "locate-path": "^3.0.0" } }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "invert-kv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", @@ -11562,20 +11598,20 @@ } }, "os-locale": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", - "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { - "execa": "^0.10.0", + "execa": "^1.0.0", "lcid": "^2.0.0", "mem": "^4.0.0" } }, "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -11602,6 +11638,16 @@ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", @@ -11613,6 +11659,12 @@ "ajv-keywords": "^3.1.0" } }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", diff --git a/package.json b/package.json index a80398b653ac9d44a095ebeb03afd7cea9f11cd5..87d561690312b3fd500b45eb93f058790a1a9454 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "webpack": "^4.28.3", "webpack-bundle-tracker": "^0.4.2-beta", "webpack-cli": "^3.1.2", - "webpack-dev-server": "^3.1.10", + "webpack-dev-server": "^3.1.14", "webpack-stream": "^5.2.1" }, "scripts": {