diff --git a/app/Charts/StatsPerYearChart.php b/app/Charts/StatsPerYearChart.php new file mode 100644 index 000000000..1d6542f8c --- /dev/null +++ b/app/Charts/StatsPerYearChart.php @@ -0,0 +1,18 @@ +select(['country_iso']) - ->where('status',"=","APPROVED") + ->where('status', "=", "APPROVED") ->groupBy('country_iso') ->get() - ->pluck('country_iso') - ; - + ->pluck('country_iso'); $countries = Country::findMany($isos)->sortBy('name'); - return $countries; } @@ -41,12 +38,11 @@ public static function withActualYearEvents() $isos = DB::table('events') ->select(['country_iso']) - ->where('status',"=","APPROVED") + ->where('status', "=", "APPROVED") ->whereYear('end_date', '>=', Carbon::now('Europe/Brussels')->year) ->groupBy('country_iso') ->get() - ->pluck('country_iso') - ; + ->pluck('country_iso'); $countries = Country::findMany($isos)->sortBy('name'); @@ -57,9 +53,14 @@ public static function withActualYearEvents() public function events() { - return $this->hasMany('App\Event'); + } + public function approvedEvents($year, $operator) + { + return $this->hasMany(Event::class) + ->where('status', '=', 'APPROVED') + ->whereYear('end_date', $operator, $year); } } diff --git a/app/Http/Controllers/StatsController.php b/app/Http/Controllers/StatsController.php new file mode 100644 index 000000000..f90fd8402 --- /dev/null +++ b/app/Http/Controllers/StatsController.php @@ -0,0 +1,226 @@ +has('yearSelector')) ? $request->input('yearSelector') : 2018; + $creatorsWithOneEvent = $this->creatorsWithAtLeastOneEvent($year); + $totalParticipants = (int)$this->totalParticipants($year); + $eventsNotReported = $this->eventsNotReported($year); + $eventsFinished = $this->eventsFinished($year); + $years = $this->getYearsArray(); + //dd($creatorsWithOneEvent); + + + + return view('stats',[ + 'creatorsWithOneEvent' => $creatorsWithOneEvent, + 'totalParticipants' => $totalParticipants, + 'eventsNotReported' => $eventsNotReported, + 'eventsFinished' => $eventsFinished, + 'years' => $years, + 'selectedYear' => $year, + 'flag' => 0 + ]); + } + + + public function getEventsPerYear(Request $request) + { + $year = ($request->has('yearSelector')) ? $request->input('yearSelector') : 2018; + $years = $this->getYearsArray(); + $total = 0; + $eventsPerYear = $this->eventsPerYear($year, null); + + $groupedEvents = $this->groupSmallEvents($eventsPerYear, $total); + $chart = $this->setPieChart(array_sort($groupedEvents), $year); + + + return view('stats', ['years' => $years, + 'eventsPerYear' => $eventsPerYear, + 'selectedYear' => $year, + 'chart' => $chart, + 'total' => $total, + 'flag' => 1 + ]); + } + + public function getEventsPerOrganiserType(Request $request) + { + $types = [ + 'Libraries' => 'library' + , 'Non profit organisations' => 'non profit' + , 'Private Businesses' => 'private business' + , 'Schools' => 'school' + ]; + + $now = Carbon::now('Europe/Brussels'); + $year = $now->year; + $type = ($request->has('typeSelector')) ? $types[$request->input('typeSelector')] : $types['Schools']; + $eventsPerYear = $this->eventsPerYear($year, $type); + $total = 0; + $groupedEvents = $this->groupSmallEvents($eventsPerYear, $total, sizeof($eventsPerYear)); + $chart = $this->setBarChart($groupedEvents, $year); + + + + return view('stats', ['types' => $types, + 'eventsPerYear' => $eventsPerYear, + 'selectedType' => array_search($type, $types), + 'chart' => $chart, + 'year' => $year, + 'flag' => 2 + ]); + } + + + private function eventsPerYear($year, $type) + { + $eventsPerYear = (!$type) + ? DB::table('countries as c') + ->selectRaw('c.name, count(c.iso) as events') + ->join('events as e', 'c.iso', '=', 'e.country_iso') + ->where('e.status', '=', 'APPROVED') + ->whereYear('e.end_date', '=', $year) + ->groupBy('c.name') + ->orderBy('events', 'desc') + ->pluck('events', 'c.name') + ->toArray() + : DB::table('countries as c') + ->selectRaw('c.name, count(c.iso) as events') + ->join('events as e', 'c.iso', '=', 'e.country_iso') + ->where('e.status', '=', 'APPROVED') + ->whereNotNull('e.organizer_type') + ->where('e.organizer_type', '=', $type) + ->whereYear('e.end_date', '>=', $year) + ->groupBy('c.name') + ->orderBy('events', 'desc') + ->pluck('events', 'c.name') + ->toArray(); + + return $eventsPerYear; + } + + + private function creatorsWithAtLeastOneEvent($year) + { + $operator = ($year == 2018) ? '>=' : '='; + $count = DB::table('events as e') + ->select() + ->where('e.status', '=', 'APPROVED') + ->whereYear('e.end_date', $operator, $year) + ->distinct() + ->count('creator_id'); + return $count; + + } + + private function totalParticipants($year) + { + $operator = ($year == 2018) ? '>=' : '='; + $count = DB::table('events as e') + ->select() + ->where('e.status', '=', 'APPROVED') + ->whereYear('e.end_date', $operator, $year) + ->where('id', '<>', 183480) + ->sum('e.participants_count'); + return $count; + + } + + private function eventsFinished($year) + { + $operator = ($year == 2018) ? '>=' : '='; + $count = DB::table('events as e') + ->select() + ->where('e.status', '=', 'APPROVED') + ->whereYear('e.end_date', $operator, $year) + ->where('e.end_date', '<=', Carbon::now('Europe/Brussels')) + ->count(); + return $count; + + } + + private function eventsNotReported($year) + { + $operator = ($year == 2018) ? '>=' : '='; + $count = DB::table('events as e') + ->select() + ->where('e.status', '=', 'APPROVED') + ->whereYear('e.start_date', $operator, $year) + ->whereYear('e.end_date', $operator, $year) + ->where('e.end_date', '<', Carbon::now('Europe/Brussels')) + ->whereNull('e.participants_count') + ->count(); + return $count; + + } + + private function setPieChart($eventsPerYear, $year) + { + $colors = ['#08a0c4', '#5e9fb2', '#819da1', '#9b9b8f', '#b1997d', '#c3976b', '#d49559', '#e39244', '#f18f2d', '#ff8c00']; + + $chart = new StatsPerYearChart(); + $chart->displayAxes(false); + $chart->height(500); + $chart->dataset('Event distribution for CodeWeek ' . $year, 'pie', array_values($eventsPerYear)) + ->backgroundColor(array_reverse($colors)); + $chart->labels(array_keys($eventsPerYear)); + return $chart; + } + + + private function setBarChart($eventsPerYear, $year) + { + $colors = ['#DBDE00', '#DBDE00', '#E1B400', '#E1B400', '#E1B400', '#E1B400', '#E1B400', + '#E1B400', '#E56F00', '#E16E03', '#DD6D06', '#D96D09', '#D56C0C', '#D16C10', '#CD6B13', + '#CD6B13', '#C96A16', '#C56A19', '#C2691D', '#BE6920', '#BA6823', '#B66826', '#B2672A', + '#AE662D', '#AA6630', '#A66533', '#A36537', '#9F643A', '#9B643D', '#976340', '#936243', + '#955D42', '#536479', '#0E70B2', '#006EBF', '#004DBF']; + + $chart = new StatsPerYearChart(); + $chart->dataset('Event distribution for CodeWeek ' . $year, 'bar', array_values($eventsPerYear)) + ->backgroundColor(array_reverse($colors)); + $chart->labels(array_keys($eventsPerYear)); + return $chart; + } + + private function getYearsArray() + { + $years = []; + $now = Carbon::now(null); + $diffYears = $now->year - 2013; + + for ($index = 0; $index < $diffYears; $index++) { + array_push($years, 2014 + $index); + } + + return $years; + } + + private function groupSmallEvents( $eventsPerYear, &$total) + { + $groupedEvents = ['Others' => 0]; + $maxCountEvents = max(array_values($eventsPerYear)); + foreach ($eventsPerYear as $country => $events) { + $total += $events; + if ($events <= (round($maxCountEvents * 0.03))) { + $groupedEvents['Others'] += $events; + continue; + } + $groupedEvents[$country] = $events; + } + return $groupedEvents; + } +} diff --git a/composer.json b/composer.json index 4977b9dbc..9884b943f 100644 --- a/composer.json +++ b/composer.json @@ -8,6 +8,7 @@ "php": "^7.1.3", "aws/aws-sdk-php": "^3.67", "caouecs/laravel-lang": "~3.0", + "consoletvs/charts": "6.*", "doctrine/dbal": "^2.8", "fideloper/proxy": "^4.0", "intervention/image": "^2.4", diff --git a/composer.lock b/composer.lock index ff13d8465..62a487622 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ce86b9554b1d17c00be2672c8a2904db", + "content-hash": "31f053a9f9aac880a3f34041f4373281", "packages": [ { "name": "anahkiasen/underscore-php", @@ -136,6 +136,49 @@ ], "time": "2018-09-25T20:15:23+00:00" }, + { + "name": "balping/json-raw-encoder", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/balping/json-raw-encoder.git", + "reference": "c404b7fc0e04ddf5d298e53105c5fc306f523195" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/balping/json-raw-encoder/zipball/c404b7fc0e04ddf5d298e53105c5fc306f523195", + "reference": "c404b7fc0e04ddf5d298e53105c5fc306f523195", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Balping\\JsonRaw\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-only" + ], + "authors": [ + { + "name": "Balázs Dura-Kovács" + } + ], + "description": "Encode arrays to json with raw JS objects (eg. callbacks) in them", + "keywords": [ + "callback", + "encode", + "json" + ], + "time": "2018-05-04T15:38:02+00:00" + }, { "name": "cakephp/chronos", "version": "1.2.2", @@ -235,6 +278,52 @@ ], "time": "2018-07-13T08:44:04+00:00" }, + { + "name": "consoletvs/charts", + "version": "6.3.4", + "source": { + "type": "git", + "url": "https://github.com/ConsoleTVs/Charts.git", + "reference": "37763eb404524296d6adc1bcfba11e01882cc11b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ConsoleTVs/Charts/zipball/37763eb404524296d6adc1bcfba11e01882cc11b", + "reference": "37763eb404524296d6adc1bcfba11e01882cc11b", + "shasum": "" + }, + "require": { + "balping/json-raw-encoder": "^1.0", + "illuminate/console": "5.*", + "illuminate/support": "5.*", + "php": ">=7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "ConsoleTVs\\Charts\\ChartsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "ConsoleTVs\\Charts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Èrik Campobadal Forés", + "email": "soc@erik.cat" + } + ], + "description": "The laravel charting package", + "time": "2018-10-13T12:27:03+00:00" + }, { "name": "dnoegel/php-xdg-base-dir", "version": "0.1", diff --git a/config/charts.php b/config/charts.php new file mode 100644 index 000000000..b1a19c4e5 --- /dev/null +++ b/config/charts.php @@ -0,0 +1,15 @@ + 'Chartjs', +]; diff --git a/package-lock.json b/package-lock.json index 757a755c9..a4ada291e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6183,6 +6183,11 @@ "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", "dev": true }, + "in-viewport": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/in-viewport/-/in-viewport-3.6.0.tgz", + "integrity": "sha512-MhaJ7Pr3NhUyAfpULysTZZBUAYfJAX1O8PccW2gvXlbQduMrJz7qQQ5yzC7SAr/0g5LbeRk432yNjsLMCnYzJg==" + }, "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", @@ -13573,14 +13578,6 @@ "integrity": "sha512-x3LV3wdmmERhVCYy3quqA57NJW7F3i6faas++pJQWtknWT+n7k30F4TVdHvCLn48peTJFRvCpxs3UuFPqgeELg==", "dev": true }, - "vue2-datepicker": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/vue2-datepicker/-/vue2-datepicker-1.9.6.tgz", - "integrity": "sha512-mjBEjAnwPsDNwAbdHP17PzwTEUTe6u64d92B316+sa7qsZOBGJMUheRkV6NZT6FfM8kbJj2gh1q5qVTTScy2sA==", - "requires": { - "vue": "2.5.16" - } - }, "ware": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz", diff --git a/package.json b/package.json index 41dbd1778..8cf7a9563 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,29 @@ { - "private": true, - "scripts": { - "dev": "npm run development", - "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "watch-poll": "npm run watch -- --watch-poll", - "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", - "prod": "npm run production", - "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" - }, - "devDependencies": { - "axios": "^0.18", - "bootstrap": "^4.1.0", - "cross-env": "^5.1", - "jquery": "^3.2", - "laravel-mix": "^2.1.11", - "lodash": "^4.17.10", - "popper.js": "^1.14.3", - "tailwindcss": "^0.5.3", - "vue": "^2.5.7" - }, - "dependencies": { - "laravel-mix-tailwind": "^0.1.0", - "vue-input-tag": "^1.0.3", - "vue-select": "^2.4.0" - } + "private": true, + "scripts": { + "dev": "npm run development", + "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", + "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", + "watch-poll": "npm run watch -- --watch-poll", + "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", + "prod": "npm run production", + "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" + }, + "devDependencies": { + "axios": "^0.18", + "bootstrap": "^4.1.0", + "cross-env": "^5.1", + "jquery": "^3.2", + "laravel-mix": "^2.1.11", + "lodash": "^4.17.10", + "popper.js": "^1.14.3", + "tailwindcss": "^0.6.6", + "vue": "^2.5.7" + }, + "dependencies": { + "in-viewport": "^3.6.0", + "laravel-mix-tailwind": "^0.1.0", + "vue-input-tag": "^1.0.3", + "vue-select": "^2.4.0" + } } diff --git a/resources/assets/js/app.js b/resources/assets/js/app.js index 45b849f3c..5cf0c351d 100644 --- a/resources/assets/js/app.js +++ b/resources/assets/js/app.js @@ -23,6 +23,7 @@ import moderateEvent from './components/ModerateEvent.vue'; import reportEvent from './components/ReportEvent.vue'; import question from './components/Question.vue'; import simpleQuestion from './components/SimpleQuestion.vue'; +import counter from './components/Counter'; @@ -48,6 +49,7 @@ const app = new Vue({ moderateEvent, reportEvent, question, - simpleQuestion + simpleQuestion, + counter } }); diff --git a/resources/assets/js/components/Counter.vue b/resources/assets/js/components/Counter.vue new file mode 100644 index 000000000..69a423ee1 --- /dev/null +++ b/resources/assets/js/components/Counter.vue @@ -0,0 +1,44 @@ + + + \ No newline at end of file diff --git a/resources/views/layout/base.blade.php b/resources/views/layout/base.blade.php index f849d82e3..748a2de0d 100755 --- a/resources/views/layout/base.blade.php +++ b/resources/views/layout/base.blade.php @@ -95,9 +95,7 @@ @include('scripts.countdown') - - - + diff --git a/resources/views/stats.blade.php b/resources/views/stats.blade.php new file mode 100644 index 000000000..4228e5952 --- /dev/null +++ b/resources/views/stats.blade.php @@ -0,0 +1,187 @@ +@extends('layout.base') + +@section('content') +
+ + +
+ @if ($flag == 0) +
+
+ + +
+ +
+
+

General metrics of {{$selectedYear}}

+
+
+
+
+
+

+ + + + + + + creators with at least 1 event. +

+
+
+

+ + + + + + + total participants. +

+
+
+
+
+

+ + + + + + + events not reported. +

+
+
+

+ + + + + + + events finished successfully. +

+
+
+ + @elseif($flag == 1) +
+
+ + +
+ +
+
+

Total events: + + events +

+
+
{!! $chart->container() !!}
+ {!! $chart->script() !!} +
+
+ + + + + + + + + + + @foreach($eventsPerYear as $country => $events) + + + + + @endforeach + + +
CountryTotal approved events
{{$country}}{{$events}}
+ @else($flag == 2) +
+
+ + +
+ +
+
+

Events organised by {{$selectedType}} in {{$year}}

+
+
{!! $chart->container() !!}
+ {!! $chart->script() !!} +
+
+ + + + + + + + + + + @foreach($eventsPerYear as $country => $events) + + + + + @endforeach + + +
CountryTotal approved events
{{$country}}{{$events}}
+ @endif + +
+
+@stop \ No newline at end of file diff --git a/resources/views/year.blade.php b/resources/views/year.blade.php new file mode 100644 index 000000000..cd598230d --- /dev/null +++ b/resources/views/year.blade.php @@ -0,0 +1,9 @@ +@extends('layout.base') + +@section('content') + +
+

{{$year}}

+
+ +@stop \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 515c0e9eb..8a483904c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -106,6 +106,24 @@ Route::get('/mail/template/creators/registered', 'MailTemplateController@registered'); Route::get('/mail/template/creators/approved', 'MailTemplateController@approved'); Route::get('/mail/template/creators/rejected', 'MailTemplateController@rejected'); + + Route::get('stats', [ + 'uses' => 'StatsController@getMetrics', + 'as' => 'stats' + ]); + + + Route::get('stats/events/years', [ + 'uses' => 'StatsController@getEventsPerYear', + 'as' => 'stats.year' + ]); + + + + Route::get('stats/events/organiser', [ + 'uses' => 'StatsController@getEventsPerOrganiserType', + 'as' => 'stats.organiser' + ]); Route::get('/mail/template/remind/creators', 'MailTemplateController@remindcreators'); }); @@ -123,5 +141,7 @@ }); + + Auth::routes(); diff --git a/yarn.lock b/yarn.lock index 66e157f31..6a0ea4a15 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1786,6 +1786,10 @@ css-what@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + css@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/css/-/css-2.2.1.tgz#73a4c81de85db664d4ee674f7d47085e3b2d55dc" @@ -3404,6 +3408,10 @@ in-publish@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" +in-viewport@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/in-viewport/-/in-viewport-3.6.0.tgz#c59b4cdcaa41adb5bf5b8fe390c7d34259891f4a" + indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" @@ -4182,11 +4190,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.4: - version "4.17.5" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" - -lodash@^4.17.10: +lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.4: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" @@ -6542,13 +6546,14 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" -tailwindcss@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-0.5.3.tgz#5444f85ab5e68a37c2bf13bc4ac097a7ee05321d" +tailwindcss@^0.6.6: + version "0.6.6" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-0.6.6.tgz#a8c8a8bf7d230c8bc10031672923d84af29cd34d" dependencies: commander "^2.11.0" + css.escape "^1.5.1" fs-extra "^4.0.2" - lodash "^4.17.4" + lodash "^4.17.5" perfectionist "^2.4.0" postcss "^6.0.9" postcss-functions "^3.0.0" @@ -7082,13 +7087,7 @@ vue-template-es2015-compiler@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18" -vue2-datepicker@*: - version "1.9.7" - resolved "https://registry.yarnpkg.com/vue2-datepicker/-/vue2-datepicker-1.9.7.tgz#3ed4db403f5af700448964c205c343d116440421" - dependencies: - vue "^2.2.1" - -vue@^2.1.8, vue@^2.2.1, vue@^2.5.7: +vue@^2.1.8, vue@^2.5.7: version "2.5.16" resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"