add calendar in dashboard

This commit is contained in:
Simon Vieille 2022-04-17 16:38:18 +02:00
parent f180f5a531
commit 46a555a038
Signed by: deblan
GPG key ID: 03383D15A1D31745
19 changed files with 659 additions and 182 deletions

View file

@ -1,111 +1,9 @@
import '../../vendor/murph/murph-core/src/core/Resources/assets/js/admin.js'
const Sortable = require('sortablejs').Sortable
const Routing = require('../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js')
const routes = require('../../public/js/fos_js_routes.json')
Routing.setRoutingData(routes)
class AddressAutocomplete {
constructor () {
this.fields = {
address: document.querySelector('#establishment_address'),
zipCode: document.querySelector('#establishment_zipCode'),
city: document.querySelector('#establishment_city')
}
this.results = []
this.timer = null
if (!this.fields.address) {
return
}
const that = this
this.fields.address.addEventListener('keyup', () => {
if (that.timer) {
clearTimeout(that.timer)
}
that.timer = setTimeout(() => {
const query = that.fields.address.value
fetch(Routing.generate('admin_establishment_address', {
q: query
}))
.then(response => response.json())
.then((data) => {
that.results = data.features
that.renderResults()
})
}, 500)
})
}
renderResults () {
const address = this.fields.address
const that = this
let wrapper = address.nextSibling
if (wrapper) {
wrapper.remove()
}
if (this.results.length === 0) {
return
}
wrapper = document.createElement('div')
wrapper.classList.add('list-group', 'mt-1', 'mb-1')
for (const address of this.results) {
const item = document.createElement('div')
item.classList.add('list-group-item', 'btn', 'text-left')
item.textContent = address.properties.label
item.setAttribute('data-name', address.properties.name)
item.setAttribute('data-zipCode', address.properties.postcode)
item.setAttribute('data-city', address.properties.city)
item.addEventListener('click', (e) => {
that.fields.address.value = e.target.getAttribute('data-name')
that.fields.zipCode.value = e.target.getAttribute('data-zipCode')
that.fields.city.value = e.target.getAttribute('data-city')
wrapper.remove()
})
wrapper.appendChild(item)
}
address.parentNode.appendChild(wrapper)
}
}
class FilesCollectionSorter {
constructor () {
const collections = document.querySelectorAll('div[data-collection^="collection-files"]')
for (const collection of collections) {
return new Sortable(collection, {
handle: '*[data-collection-item]',
sort: true,
animation: 150,
fallbackTolerance: 3,
onEnd: (e) => {
const positions = collection.querySelectorAll('*[data-collection-item] input[type=hidden]')
console.log(positions);
for (let u = 0; u < positions.length; u++) {
positions[u].value = u
}
}
})
}
}
}
const AddressAutocomplete = require('./modules/address.js')
const FilesCollectionSorter = require('./modules/collection-sorter.js')
const Calendar = require('./modules/calendar.js')
new AddressAutocomplete()
new FilesCollectionSorter()
new Calendar()

View file

@ -0,0 +1,150 @@
<template>
<div>
<div class="row">
<div class="col-md-4">
<div class="pr-4">
<label for="projects">Période</label>
<div class="row">
<div class="col-6">
<select class="form-control mr-2" v-model="month">
<option v-for="value, key in months" v-bind:value="key">{{ value }}</option>
</select>
</div>
<div class="col-6">
<select class="form-control ml-2" v-model="year">
<option v-for="value, key in years" v-bind:value="key">{{ value }}</option>
</select>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="pr-2">
<label for="projects">Intervenants</label>
<select class="form-control" v-model="selectedSpeakers" id="speakers" multiple>
<option v-for="item in speakers" v-bind:value="item.id">{{ item.name }}</option>
</select>
</div>
</div>
<div class="col-md-4">
<div>
<label for="projects">Projets</label>
<select class="form-control" v-model="selectedProjects" id="projects" multiple>
<option v-for="item in projects" v-bind:value="item.id">{{ item.label }}</option>
</select>
</div>
</div>
</div>
<div class="row" v-if="weeks.length > 0">
<div class="col-md-12">
<table class="table table-bordered mt-3">
<Week v-for="week, key in weeks" v-bind:key="key" v-bind:week="week" />
</table>
</div>
</div>
</div>
</template>
<style scoped>
table {
font-size: 12px;
}
</style>
<script>
import Routing from '../../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js'
const Week = require('./Week').default
const Choices = require('choices.js')
const chunk = require('chunk')
const axios = require('axios').default
const routes = require('../../../public/js/fos_js_routes.json')
Routing.setRoutingData(routes)
export default {
name: 'Calendar',
components: {
Week
},
data () {
return {
projects: [],
speakers: [],
month: new Date().getMonth() + 1,
year: new Date().getFullYear(),
years: {},
weeks: [],
months: {
1: 'Janvier',
2: 'Février',
3: 'Mars',
4: 'Avril',
5: 'Mai',
6: 'Juin',
7: 'Juillet',
8: 'Août',
9: 'Septembre',
10: 'Octobre',
11: 'Novembre',
12: 'Décembre'
},
selectedProjects: [],
selectedSpeakers: []
}
},
methods: {
refresh () {
const that = this
axios.get(Routing.generate('admin_calendar_month', {
month: this.month,
year: this.year,
projects: this.selectedProjects,
speakers: this.selectedSpeakers
}))
.then((response) => {
that.weeks = chunk(response.data, 7)
})
}
},
mounted () {
const that = this
for (let year = 2020; year <= (new Date().getFullYear() + 3); year++) {
this.years[year] = year
}
axios.get(Routing.generate('admin_calendar_projects'))
.then((response) => {
that.projects = response.data
that.selectedProjects = response.data.map(item => item.id)
window.setTimeout(e => new Choices(document.getElementById('projects')), 500)
})
axios.get(Routing.generate('admin_calendar_speakers'))
.then((response) => {
that.speakers = response.data
that.selectedSpeakers = response.data.map(item => item.id)
window.setTimeout(e => new Choices(document.getElementById('speakers')), 500)
})
},
watch: {
year (year) {
this.refresh()
},
month (month) {
this.refresh()
},
selectedProjects (selectedProjects) {
this.refresh()
},
selectedSpeakers (selectedSpeakers) {
this.refresh()
}
}
}
</script>

View file

@ -0,0 +1,95 @@
<template>
<fragment>
<thead class="thead-light">
<tr>
<th v-for="item in week" v-bind:class="{'text-right': true, 'text-muted': !item.currentMonth}">
{{ item.day }}
</th>
</tr>
</thead>
<tr>
<td v-for="item in week" v-bind:class="{'bg-light': !item.currentMonth}">
<div v-for="event in item.events">
<details v-if="event.description">
<summary>
<span class="font-weight-bold">
<span class="text-muted">
[{{ event.startAt }}]
</span>
{{ event.summary }}
</span>
</summary>
<p v-html="event.description.replace(/\n/g, '<br>')" class="mt-3"></p>
<div class="mt-3">
<a v-bind:href="route('admin_speaker_show', {entity: speaker.id})" v-for="speaker in event.speakers" v-bind:class="['d-block mr-1 mt-1 btn btn-xs', 'btn-' + speaker.color].join(' ')">
{{ speaker.name }}
</a>
<a v-bind:href="route('admin_project_show', {entity: project.id})" v-for="project in event.projects" class="d-block mr-1 mt-1 btn btn-xs border btn-light">
{{ project.label }}
</a>
</div>
</details>
<div v-else>
<span class="font-weight-bold">{{ event.summary }}</span>
<div class="mt-3">
<a v-bind:href="route('admin_speaker_show', {entity: speaker.id})" v-for="speaker in event.speakers" v-bind:class="['d-block mr-1 mt-1 btn btn-xd', 'btn-' + speaker.color].join(' ')">
{{ speaker.name }}
</a>
<a v-bind:href="route('admin_project_show', {entity: project.id})" v-for="project in event.projects" class="d-block mr-1 mt-1 btn btn-xs border btn-light">
{{ project.label }}
</a>
</div>
</div>
</div>
</td>
</tr>
</fragment>
</template>
<style scoped>
td {
height: calc((100vh - 190px) / 8);
width: calc(100% / 7);
}
.btn-xs {
font-size: 12px;
padding: 3px 4px;
}
</style>
<script>
import Routing from '../../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js'
import { Fragment } from 'vue-fragment'
const routes = require('../../../public/js/fos_js_routes.json')
Routing.setRoutingData(routes)
export default {
name: 'Week',
components: {
Fragment
},
props: {
week: {
type: Array,
required: true
}
},
methods: {
route(route, params) {
return Routing.generate(route, params)
}
},
data () {
return {
}
}
}
</script>

View file

@ -0,0 +1,84 @@
const Routing = require('../../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js')
const routes = require('../../../public/js/fos_js_routes.json')
Routing.setRoutingData(routes)
class AddressAutocomplete {
constructor () {
this.fields = {
address: document.querySelector('#establishment_address'),
zipCode: document.querySelector('#establishment_zipCode'),
city: document.querySelector('#establishment_city')
}
this.results = []
this.timer = null
if (!this.fields.address) {
return
}
const that = this
this.fields.address.addEventListener('keyup', () => {
if (that.timer) {
clearTimeout(that.timer)
}
that.timer = setTimeout(() => {
const query = that.fields.address.value
fetch(Routing.generate('admin_establishment_address', {
q: query
}))
.then(response => response.json())
.then((data) => {
that.results = data.features
that.renderResults()
})
}, 500)
})
}
renderResults () {
const address = this.fields.address
const that = this
let wrapper = address.nextSibling
if (wrapper) {
wrapper.remove()
}
if (this.results.length === 0) {
return
}
wrapper = document.createElement('div')
wrapper.classList.add('list-group', 'mt-1', 'mb-1')
for (const address of this.results) {
const item = document.createElement('div')
item.classList.add('list-group-item', 'btn', 'text-left')
item.textContent = address.properties.label
item.setAttribute('data-name', address.properties.name)
item.setAttribute('data-zipCode', address.properties.postcode)
item.setAttribute('data-city', address.properties.city)
item.addEventListener('click', (e) => {
that.fields.address.value = e.target.getAttribute('data-name')
that.fields.zipCode.value = e.target.getAttribute('data-zipCode')
that.fields.city.value = e.target.getAttribute('data-city')
wrapper.remove()
})
wrapper.appendChild(item)
}
address.parentNode.appendChild(wrapper)
}
}
module.exports = AddressAutocomplete

View file

@ -0,0 +1,17 @@
const Vue = require('vue').default
const Calendar = require('../components/Calendar').default
module.exports = () => {
if (!document.getElementById('calendar')) {
return
}
return new Vue({
el: '#calendar',
template: '<Calendar />',
components: {
Calendar
}
})
}

View file

@ -0,0 +1,26 @@
const Sortable = require('sortablejs').Sortable
class FilesCollectionSorter {
constructor () {
const collections = document.querySelectorAll('div[data-collection^="collection-files"]')
for (const collection of collections) {
return new Sortable(collection, {
handle: '*[data-collection-item]',
sort: true,
animation: 150,
fallbackTolerance: 3,
onEnd: (e) => {
const positions = collection.querySelectorAll('*[data-collection-item] input[type=hidden]')
console.log(positions)
for (let u = 0; u < positions.length; u++) {
positions[u].value = u
}
}
})
}
}
}
module.exports = FilesCollectionSorter

View file

@ -8,6 +8,8 @@
"build": "encore production --progress"
},
"dependencies": {
"murph-project": "^1"
"chunk": "^0.0.3",
"murph-project": "^1",
"vue-fragment": "^1.5.2"
}
}

View file

@ -0,0 +1,123 @@
<?php
namespace App\Controller;
use App\Repository\EventRepositoryQuery;
use App\Repository\ProjectRepositoryQuery;
use App\Repository\SpeakerRepositoryQuery;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class CalendarAdminController extends AbstractController
{
#[Route('/admin/calendar/month', name: 'admin_calendar_month', options: ['expose' => true])]
public function month(
Request $request,
SpeakerRepositoryQuery $speakerQuery,
ProjectRepositoryQuery $projectQuery,
EventRepositoryQuery $eventQuery
): Response {
$projects = $projectQuery->create()->withIds($request->query->get('projects'))->find();
$speakers = $speakerQuery->create()->withIds($request->query->get('speakers'))->find();
$month = $request->query->get('month', date('m'));
$year = $request->query->get('year', date('Y'));
$dateFrom = new \DateTime(sprintf('%d-%d-1', $year, $month));
$currentMonthFirstDayOfWeek = $dateFrom->format('N');
$nextMonthFirstDayOfWeek = date('N', $dateFrom->getTimestamp() + 3600 * 24 * $dateFrom->format('t'));
$currentMonthDays = $dateFrom->format('t');
$prevMonthDays = date('t', $dateFrom->getTimestamp() - 3600);
$days = [];
if ($currentMonthFirstDayOfWeek > 1) {
for ($day = $prevMonthDays - $currentMonthFirstDayOfWeek + 2; $day <= $prevMonthDays; ++$day) {
$days[] = [
'day' => $day,
'currentMonth' => false,
'events' => [],
];
}
}
for ($day = 1; $day <= $currentMonthDays; ++$day) {
$events = $eventQuery->create()
->ofProjects($projects)
->ofSpeakers($speakers)
->ofMonth($month)
->ofYear($year)
->ofDay($day)
->find()
;
$events = array_map(function ($event) {
return [
'uid' => $event->getUid(),
'summary' => $event->getSummary(),
'description' => $event->getCleanedDescription(),
'startAt' => $event->getStartAt() ? $event->getStartAt()->format('H:i') : null,
'finishAt' => $event->getFinishAt() ? $event->getFinishAt()->format('H:i') : null,
'projects' => array_map(fn ($i) => [
'id' => $i->getId(),
'label' => $i->getLabel()
], $event->getProjects()->toArray()),
'speakers' => array_map(fn ($i) => [
'id' => $i->getId(),
'name' => $i->getName(),
'color' => $i->getColor(),
], $event->getSpeakers()->toArray()),
];
}, $events);
$days[] = [
'day' => $day,
'currentMonth' => true,
'events' => $events,
];
}
if ($nextMonthFirstDayOfWeek > 1) {
$day = 0;
for ($u = $nextMonthFirstDayOfWeek; $u <= 7; ++$u) {
$days[] = [
'day' => ++$day,
'currentMonth' => false,
'events' => [],
];
}
}
return $this->json($days);
}
#[Route('/admin/calendar/projects', name: 'admin_calendar_projects', options: ['expose' => true])]
public function projects(ProjectRepositoryQuery $query): Response
{
$entities = array_map(function ($entity) {
return [
'id' => $entity->getId(),
'label' => $entity->getLabel(),
];
}, $query->create()->orderBy('.label')->find());
return $this->json($entities);
}
#[Route('/admin/calendar/speakers', name: 'admin_calendar_speakers', options: ['expose' => true])]
public function speakers(SpeakerRepositoryQuery $query): Response
{
$entities = array_map(function ($entity) {
return [
'id' => $entity->getId(),
'name' => $entity->getName(),
];
}, $query->create()->orderBy('.name')->find());
return $this->json($entities);
}
}

View file

@ -35,7 +35,7 @@ class ProjectAdminController extends CrudController
}
/**
* @Route("/admin/project/show/{entity}", name="admin_project_show", methods={"GET"})
* @Route("/admin/project/show/{entity}", name="admin_project_show", methods={"GET"}, options={"expose":true})
*/
public function show(Entity $entity): Response
{

View file

@ -38,7 +38,7 @@ class SpeakerAdminController extends CrudController
}
/**
* @Route("/admin/speaker/show/{entity}", name="admin_speaker_show", methods={"GET"})
* @Route("/admin/speaker/show/{entity}", name="admin_speaker_show", methods={"GET"}, options={"expose":true})
*/
public function show(Entity $entity): Response
{

View file

@ -45,20 +45,20 @@ class Event implements EntityInterface
*/
protected $finishAt;
/**
* @ORM\ManyToOne(targetEntity=Project::class, inversedBy="events", cascade={"persist", "remove"})
* @ORM\JoinColumn(nullable=false)
*/
protected $projects;
/**
* @ORM\ManyToMany(targetEntity=Speaker::class, inversedBy="events")
*/
private $speakers;
/**
* @ORM\ManyToMany(targetEntity=Project::class, inversedBy="events")
*/
private $projects;
public function __construct()
{
$this->speakers = new ArrayCollection();
$this->projects = new ArrayCollection();
}
public function getId(): ?int
@ -131,18 +131,6 @@ class Event implements EntityInterface
return $this;
}
public function getProjects(): ?Project
{
return $this->projects;
}
public function setProjects(?Project $projects): self
{
$this->projects = $projects;
return $this;
}
/**
* @return Collection<int, Speaker>
*/
@ -166,4 +154,28 @@ class Event implements EntityInterface
return $this;
}
/**
* @return Collection<int, Project>
*/
public function getProjects(): Collection
{
return $this->projects;
}
public function addProject(Project $project): self
{
if (!$this->projects->contains($project)) {
$this->projects[] = $project;
}
return $this;
}
public function removeProject(Project $project): self
{
$this->projects->removeElement($project);
return $this;
}
}

View file

@ -51,9 +51,9 @@ class Project implements EntityInterface
protected $debriefings;
/**
* @ORM\OneToMany(targetEntity=Event::class, mappedBy="projects", cascade={"persist", "remove"})
* @ORM\ManyToMany(targetEntity=Event::class, mappedBy="projects")
*/
protected $events;
private $events;
public function __construct()
{
@ -185,20 +185,14 @@ class Project implements EntityInterface
*/
public function getEvents(): Collection
{
$events = $this->events->toArray();
usort($events, function($a, $b) {
return $a->getStartAt() <=> $a->getFinishAt();
});
return new ArrayCollection($events);
return $this->events;
}
public function addEvent(Event $event): self
{
if (!$this->events->contains($event)) {
$this->events[] = $event;
$event->setProjects($this);
$event->addProject($this);
}
return $this;
@ -207,10 +201,7 @@ class Project implements EntityInterface
public function removeEvent(Event $event): self
{
if ($this->events->removeElement($event)) {
// set the owning side to null (unless already changed)
if ($event->getProjects() === $this) {
$event->setProjects(null);
}
$event->removeProject($this);
}
return $this;

View file

@ -56,6 +56,11 @@ class Speaker implements EntityInterface, EncryptedEntityInterface
*/
private $events;
/**
* @ORM\Column(type="string", length=30, nullable=true)
*/
private $color;
public function __construct()
{
$this->interventions = new ArrayCollection();
@ -192,4 +197,16 @@ class Speaker implements EntityInterface, EncryptedEntityInterface
return $this;
}
public function getColor(): ?string
{
return $this->color;
}
public function setColor(?string $color): self
{
$this->color = $color;
return $this;
}
}

View file

@ -7,6 +7,7 @@ use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class SpeakerType extends AbstractType
{
@ -14,6 +15,17 @@ class SpeakerType extends AbstractType
{
$builder
->add('name')
->add('color', ChoiceType::class, [
'required' => true,
'choices' => [
'Bleu' => 'primary',
'Gris' => 'secondary',
'Vert' => 'success',
'Rouge' => 'danger',
'Jaune' => 'warning',
'Turquoise' => 'info',
],
])
;
if ($options['edit_caldav']) {

View file

@ -3,8 +3,8 @@
namespace App\Repository;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use App\Repository\EventRepository as Repository;
use Knp\Component\Pager\PaginatorInterface;
class EventRepositoryQuery extends RepositoryQuery
{
@ -12,4 +12,54 @@ class EventRepositoryQuery extends RepositoryQuery
{
parent::__construct($repository, 'e', $paginator);
}
public function ofProjects(array $projects): self
{
if (empty($projects)) {
return $this;
}
return $this
->innerJoin('.projects', 'p')
->andWhere('p.id IN (:projectIds)')
->setParameter('projectIds', array_map(fn ($i) => $i->getId(), $projects))
;
}
public function ofSpeakers(array $speakers): self
{
if (empty($speakers)) {
return $this;
}
return $this
->innerJoin('.speakers', 's')
->andWhere('s.id IN (:speakerIds)')
->setParameter('speakerIds', array_map(fn ($i) => $i->getId(), $speakers))
;
}
public function ofMonth(int $month): self
{
return $this
->andWhere('.startAt LIKE :month')
->setParameter('month', '%-'.sprintf('%02d', $month).'-% %')
;
}
public function ofYear(int $year): self
{
return $this
->andWhere('.startAt LIKE :year')
->setParameter('year', $year.'-%-% %')
;
}
public function ofDay(int $day): self
{
return $this
->andWhere('.startAt LIKE :day')
->setParameter('day', '%-%-'.sprintf('%02d', $day).' %')
;
}
}

View file

@ -12,4 +12,16 @@ class ProjectRepositoryQuery extends RepositoryQuery
{
parent::__construct($repository, 'p', $paginator);
}
public function withIds(?array $ids)
{
if (null === $ids) {
return $this;
}
return $this
->andWhere('.id IN (:ids)')
->setParameter('ids', $ids)
;
}
}

View file

@ -13,6 +13,18 @@ class SpeakerRepositoryQuery extends RepositoryQuery
parent::__construct($repository, 's', $paginator);
}
public function withIds(?array $ids)
{
if (null === $ids) {
return $this;
}
return $this
->andWhere('.id IN (:ids)')
->setParameter('ids', $ids)
;
}
public function withCalendar()
{
return $this

View file

@ -1,12 +1,10 @@
{% extends '@Core/admin/layout.html.twig' %}
{% block body %}
<div class="bg-light pl-5 pr-4 pt-5 pb-5">
<div class="d-flex">
<div class="mr-auto w-50">
<h1 class="display-5">
Tableau de bord
</h1>
<div class="row">
<div class="col-12">
<div class="p-3">
<div id="calendar"></div>
</div>
</div>
</div>

View file

@ -2246,6 +2246,11 @@ chrome-trace-event@^1.0.2:
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
chunk@^0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/chunk/-/chunk-0.0.3.tgz#d4f7473e35439c543d1d209464c27839e2b51d7a"
integrity sha512-oGfwvhjGRW3Ks4GTdGoJhZWKEO1eomjOC26001R+5H0TIlP7vBCO+/XcNcPCA6ayYC7RQSq1/NsN4679Odcm5A==
ci-info@^1.5.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
@ -2800,7 +2805,7 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "2.1.2"
debuglog@*, debuglog@^1.0.1:
debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
@ -4250,7 +4255,7 @@ import-local@^3.0.2:
pkg-dir "^4.2.0"
resolve-cwd "^3.0.0"
imurmurhash@*, imurmurhash@^0.1.4:
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
@ -4966,11 +4971,6 @@ lockfile@^1.0.4:
dependencies:
signal-exit "^3.0.2"
lodash._baseindexof@*:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
@ -4979,33 +4979,11 @@ lodash._baseuniq@~4.6.0:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
lodash._bindcallback@*:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
lodash._cacheindexof@*:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
lodash._createcache@*:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
dependencies:
lodash._getnative "^3.0.0"
lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
lodash._getnative@*, lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
lodash._root@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
@ -5036,11 +5014,6 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash.restparam@*:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
lodash.truncate@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
@ -8024,6 +7997,11 @@ vue-eslint-parser@^7.10.0:
lodash "^4.17.21"
semver "^6.3.0"
vue-fragment@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/vue-fragment/-/vue-fragment-1.5.2.tgz#310017170c564c4aad95da14c185c92c6784fd3c"
integrity sha512-KEW0gkeNOLJjtXN4jqJhTazez5jtrwimHkE5Few/VxblH4F9EcvJiEsahrV5kg5uKd5U8du4ORKS6QjGE0piYA==
vue-hot-reload-api@^2.3.0:
version "2.3.4"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"