Merge pull request #970 from gradido/707-Separate-Account-Overview-from-Send-Form

707 separate account overview and send
This commit is contained in:
Alexander Friedland 2021-10-22 09:08:55 +02:00 committed by GitHub
commit 55c8173223
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 423 additions and 330 deletions

View File

@ -27,7 +27,6 @@
"babel-preset-vue": "^2.0.2",
"bootstrap": "4.3.1",
"bootstrap-vue": "^2.5.0",
"chart.js": "^2.9.3",
"d3": "^5.7.0",
"datamaps": "^0.5.9",
"date-fns": "^1.30.1",
@ -64,7 +63,6 @@
"vue": "^2.6.11",
"vue-apollo": "^3.0.7",
"vue-bootstrap-typeahead": "^0.2.6",
"vue-chartjs": "^3.5.0",
"vue-cli-plugin-i18n": "^1.0.1",
"vue-clickaway": "^2.2.2",
"vue-clipboard2": "^0.3.0",

View File

@ -0,0 +1,45 @@
import { mount } from '@vue/test-utils'
import Status from './Status'
const localVue = global.localVue
describe('Status', () => {
let wrapper
const mocks = {
$n: jest.fn((n) => n),
}
const propsData = {
balance: 1234,
statusText: 'GDD',
}
const Wrapper = () => {
return mount(Status, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
describe('balance is pending', () => {
it('it displays an en-dash', () => {
expect(wrapper.find('div.gdd-status-div').text()).toEqual('— GDD')
})
})
describe('balance is loaded', () => {
beforeEach(() => {
wrapper.setProps({
pending: false,
})
})
it('it displays the ammount of GDD', () => {
expect(wrapper.find('div.gdd-status-div').text()).toEqual('1234 GDD')
})
})
})
})

View File

@ -0,0 +1,24 @@
<template>
<div class="gdd-status">
<div class="p-0 gdd-status-div">
{{ pending ? '—' : $n(balance, 'decimal') }} {{ statusText }}
</div>
</div>
</template>
<script>
export default {
name: 'Status',
props: {
balance: { type: Number, default: 0 },
pending: {
type: Boolean,
default: true,
},
statusText: {
type: String,
default: '',
},
},
}
</script>

View File

@ -110,6 +110,7 @@
"logout": "Abmelden",
"members_area": "Mitgliederbereich",
"message": "hallo gradido !!",
"overview": "Übersicht",
"privacy_policy": "Datenschutzerklärung",
"send": "Senden",
"settings": {

View File

@ -110,6 +110,7 @@
"logout": "Logout",
"members_area": "Member's area",
"message": "hello gradido !!",
"overview": "Overview",
"privacy_policy": "Privacy policy",
"send": "Send",
"settings": {

View File

@ -6,6 +6,7 @@ import { loadAllRules } from './validation-rules'
import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost'
import VueApollo from 'vue-apollo'
import CONFIG from './config'
import addNavigationGuards from './routes/guards'
import { store } from './store/store'

View File

@ -49,8 +49,8 @@ describe('router', () => {
expect(routes.find((r) => r.path === '/').redirect()).toEqual({ path: '/login' })
})
it('has twelve routes defined', () => {
expect(routes).toHaveLength(13)
it('has fourteen routes defined', () => {
expect(routes).toHaveLength(14)
})
describe('overview', () => {
@ -64,6 +64,17 @@ describe('router', () => {
})
})
describe('send', () => {
it('requires authorization', () => {
expect(routes.find((r) => r.path === '/send').meta.requiresAuth).toBeTruthy()
})
it('loads the "Send" component', async () => {
const component = await routes.find((r) => r.path === '/send').component()
expect(component.default.name).toBe('SendOverview')
})
})
describe('profile', () => {
it('requires authorization', () => {
expect(routes.find((r) => r.path === '/profile').meta.requiresAuth).toBeTruthy()

View File

@ -14,6 +14,13 @@ const routes = [
requiresAuth: true,
},
},
{
path: '/send',
component: () => import('../views/Pages/SendOverview.vue'),
meta: {
requiresAuth: true,
},
},
{
path: '/profile',
component: () => import('../views/Pages/UserProfileOverview.vue'),

View File

@ -89,33 +89,40 @@ describe('DashboardLayoutGdd', () => {
navbar = wrapper.findAll('ul.navbar-nav').at(0)
})
it('has three items in the navbar', () => {
expect(navbar.findAll('ul > a')).toHaveLength(3)
it('has four items in the navbar', () => {
expect(navbar.findAll('ul > a')).toHaveLength(4)
})
it('has first item "send" in navbar', () => {
expect(navbar.findAll('ul > a').at(0).text()).toEqual('send')
it('has first item "overview" in navbar', () => {
expect(navbar.findAll('ul > a').at(0).text()).toEqual('overview')
})
it('has first item "send" linked to overview in navbar', () => {
navbar.findAll('ul > a').at(0).trigger('click')
expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/overview')
it('has first item "overview" linked to overview in navbar', () => {
expect(navbar.findAll('ul > a > a').at(0).attributes('href')).toBe('/overview')
})
it('has second item "transactions" in navbar', () => {
expect(navbar.findAll('ul > a').at(1).text()).toEqual('transactions')
it('has second item "send" in navbar', () => {
expect(navbar.findAll('ul > a').at(1).text()).toEqual('send')
})
it('has second item "transactions" linked to transactions in navbar', async () => {
expect(wrapper.findAll('a').at(3).attributes('href')).toBe('/transactions')
it('has second item "send" linked to /send in navbar', () => {
expect(wrapper.findAll('ul > a > a').at(1).attributes('href')).toBe('/send')
})
it('has three items in the navbar', () => {
expect(navbar.findAll('ul > a')).toHaveLength(3)
it('has third item "transactions" in navbar', () => {
expect(navbar.findAll('ul > a').at(2).text()).toEqual('transactions')
})
it('has third item "My profile" linked to profile in navbar', async () => {
expect(wrapper.findAll('a').at(5).attributes('href')).toBe('/profile')
it('has third item "transactions" linked to transactions in navbar', async () => {
expect(wrapper.findAll('ul > a > a').at(2).attributes('href')).toBe('/transactions')
})
it('has fourth item "My profile" in navbar', () => {
expect(navbar.findAll('ul > a').at(3).text()).toEqual('site.navbar.my-profil')
})
it('has fourth item "My profile" linked to profile in navbar', async () => {
expect(wrapper.findAll('ul > a > a').at(3).attributes('href')).toBe('/profile')
})
it('has a link to the members area', () => {

View File

@ -2,11 +2,16 @@
<div>
<side-bar @logout="logout" :balance="balance" :pending="pending">
<template slot="links">
<p></p>
<sidebar-item
:link="{
name: $t('overview'),
path: '/overview',
}"
></sidebar-item>
<sidebar-item
:link="{
name: $t('send'),
path: '/overview',
path: '/send',
}"
></sidebar-item>
<sidebar-item

View File

@ -1,9 +1,6 @@
import { mount } from '@vue/test-utils'
import AccountOverview from './AccountOverview'
const sendMock = jest.fn()
sendMock.mockResolvedValue('success')
const localVue = global.localVue
window.scrollTo = jest.fn()
@ -11,26 +8,16 @@ window.scrollTo = jest.fn()
describe('AccountOverview', () => {
let wrapper
const propsData = {
balance: 123.45,
transactionCount: 1,
}
const mocks = {
$t: jest.fn((t) => t),
$n: jest.fn((n) => String(n)),
$store: {
state: {
email: 'sender@example.org',
},
},
$apollo: {
mutate: sendMock,
},
$n: jest.fn(),
}
const Wrapper = () => {
return mount(AccountOverview, { localVue, mocks, propsData })
return mount(AccountOverview, {
localVue,
mocks,
})
}
describe('mount', () => {
@ -38,97 +25,21 @@ describe('AccountOverview', () => {
wrapper = Wrapper()
})
it('has a status line', () => {
expect(wrapper.find('div.gdd-status').exists()).toBeTruthy()
it('has a status gdd-status-gdd', () => {
expect(wrapper.find('div.gdd-status-gdd').exists()).toBeTruthy()
})
it('has a send field', () => {
expect(wrapper.find('div.gdd-send').exists()).toBeTruthy()
it('has a status gdd-status-gdt', () => {
expect(wrapper.find('div.gdd-status-gdt').exists()).toBeTruthy()
})
it('has a transactions table', () => {
expect(wrapper.find('div.gdd-transaction-list').exists()).toBeTruthy()
})
describe('transaction form', () => {
it('steps forward in the dialog', async () => {
await wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
})
expect(wrapper.findComponent({ name: 'TransactionConfirmation' }).exists()).toBeTruthy()
})
})
describe('confirm transaction', () => {
beforeEach(() => {
wrapper.setData({
currentTransactionStep: 1,
transactionData: {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
},
})
})
it('resets the transaction process when on-reset is emitted', async () => {
await wrapper.findComponent({ name: 'TransactionConfirmation' }).vm.$emit('on-reset')
expect(wrapper.findComponent({ name: 'TransactionForm' }).exists()).toBeTruthy()
expect(wrapper.vm.transactionData).toEqual({
email: '',
amount: 0,
memo: '',
})
})
describe('transaction is confirmed and server response is success', () => {
beforeEach(async () => {
jest.clearAllMocks()
await wrapper
.findComponent({ name: 'TransactionConfirmation' })
.vm.$emit('send-transaction')
})
it('calls the API when send-transaction is emitted', async () => {
expect(sendMock).toBeCalledWith(
expect.objectContaining({
variables: {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
},
}),
)
})
it('emits update-balance', () => {
expect(wrapper.emitted('update-balance')).toBeTruthy()
expect(wrapper.emitted('update-balance')).toEqual([[23.45]])
})
it('shows the succes page', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_success')
})
})
describe('transaction is confirmed and server response is error', () => {
beforeEach(async () => {
jest.clearAllMocks()
sendMock.mockRejectedValue({ message: 'receiver not found' })
await wrapper
.findComponent({ name: 'TransactionConfirmation' })
.vm.$emit('send-transaction')
})
it('shows the error page', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_error')
})
it('shows recipient not found', () => {
expect(wrapper.text()).toContain('transaction.receiverNotFound')
})
describe('timestamp updates', () => {
it('emits update transactions', async () => {
expect(wrapper.emitted('update-transactions')).toHaveLength(1)
await wrapper.setData({ timestamp: Date.now() })
expect(wrapper.emitted('update-transactions')).toHaveLength(2)
})
})
})

View File

@ -1,83 +1,59 @@
<template>
<div>
<b-container fluid>
<gdd-status
v-if="showContext"
:pending="pending"
:balance="balance"
:gdt-balance="GdtBalance"
/>
<b-row>
<b-col class="col-6">
<b-row>
<b-col class="col-11 bg-gray text-white p-3">
<status
class="gdd-status-gdd"
:pending="pending"
:balance="balance"
status-text="GDD"
/>
</b-col>
</b-row>
</b-col>
<b-col class="col-6 text-right">
<b-row>
<b-col class="bg-white text-gray p-3">
<status
class="gdd-status-gdt"
:pending="pending"
:balance="GdtBalance"
status-text="GDT"
/>
</b-col>
</b-row>
</b-col>
</b-row>
<br />
<gdd-send :currentTransactionStep="currentTransactionStep">
<template #transaction-form>
<transaction-form :balance="balance" @set-transaction="setTransaction"></transaction-form>
</template>
<template #transaction-confirmation>
<transaction-confirmation
:email="transactionData.email"
:amount="transactionData.amount"
:memo="transactionData.memo"
:loading="loading"
@send-transaction="sendTransaction"
@on-reset="onReset"
></transaction-confirmation>
</template>
<template #transaction-result>
<transaction-result
:error="error"
:errorResult="errorResult"
@on-reset="onReset"
></transaction-result>
</template>
</gdd-send>
<hr />
<gdd-transaction-list
v-if="showContext"
:transactions="transactions"
:pageSize="5"
:timestamp="timestamp"
:transaction-count="transactionCount"
@update-transactions="updateTransactions"
/>
<gdd-transaction-list-footer v-if="showContext" :count="transactionCount" />
<gdd-transaction-list-footer :count="transactionCount" />
</b-container>
</div>
</template>
<script>
import GddStatus from './AccountOverview/GddStatus.vue'
import GddSend from './AccountOverview/GddSend.vue'
import Status from '../../components/Status.vue'
import GddTransactionList from './AccountOverview/GddTransactionList.vue'
import GddTransactionListFooter from './AccountOverview/GddTransactionListFooter.vue'
import TransactionForm from './AccountOverview/GddSend/TransactionForm.vue'
import TransactionConfirmation from './AccountOverview/GddSend/TransactionConfirmation.vue'
import TransactionResult from './AccountOverview/GddSend/TransactionResult.vue'
import { sendCoins } from '../../graphql/mutations.js'
const EMPTY_TRANSACTION_DATA = {
email: '',
amount: 0,
memo: '',
}
export default {
name: 'Overview',
components: {
GddStatus,
GddSend,
Status,
GddTransactionList,
GddTransactionListFooter,
TransactionForm,
TransactionConfirmation,
TransactionResult,
},
data() {
return {
timestamp: Date.now(),
transactionData: { ...EMPTY_TRANSACTION_DATA },
error: false,
errorResult: '',
currentTransactionStep: 0,
loading: false,
}
},
props: {
@ -92,38 +68,7 @@ export default {
default: true,
},
},
computed: {
showContext() {
return this.currentTransactionStep === 0
},
},
methods: {
setTransaction(data) {
this.transactionData = { ...data }
this.currentTransactionStep = 1
},
async sendTransaction() {
this.loading = true
this.$apollo
.mutate({
mutation: sendCoins,
variables: this.transactionData,
})
.then(() => {
this.error = false
this.$emit('update-balance', this.transactionData.amount)
})
.catch((err) => {
this.errorResult = err.message
this.error = true
})
this.currentTransactionStep = 2
this.loading = false
},
onReset() {
this.transactionData = { ...EMPTY_TRANSACTION_DATA }
this.currentTransactionStep = 0
},
updateTransactions(pagination) {
this.$emit('update-transactions', pagination)
},

View File

@ -1,53 +0,0 @@
import { mount } from '@vue/test-utils'
import GddStatus from './GddStatus'
const localVue = global.localVue
describe('GddStatus', () => {
let wrapper
const mocks = {
$n: jest.fn((n) => n),
}
const propsData = {
balance: 1234,
GdtBalance: 9876,
}
const Wrapper = () => {
return mount(GddStatus, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
describe('balance is loading', () => {
it('it displays em-dash as the ammount of GDD', () => {
expect(wrapper.findAll('div.card-body').at(0).text()).toEqual('— GDD')
})
it('it displays em-dash as the ammount of GDT', () => {
expect(wrapper.findAll('div.card-body').at(1).text()).toEqual('— GDT')
})
})
describe('balance is loaded', () => {
beforeEach(() => {
wrapper.setProps({
pending: false,
})
})
it('it displays the ammount of GDD', () => {
expect(wrapper.findAll('div.card-body').at(0).text()).toEqual('1234 GDD')
})
it('it displays the ammount of GDT', () => {
expect(wrapper.findAll('div.card-body').at(1).text()).toEqual('9876 GDT')
})
})
})
})

View File

@ -1,30 +0,0 @@
<template>
<div class="gdd-status">
<b-row>
<b-col class="p-0">
<b-card class="p-0" style="background-color: #ebebeba3 !important">
{{ pending ? '—' : $n(balance, 'decimal') }} GDD
</b-card>
</b-col>
<b-col class="pr-0">
<b-card class="p-0 text-right" style="background-color: #ebebeba3 !important">
{{ pending ? '—' : $n(GdtBalance, 'decimal') }} GDT
</b-card>
</b-col>
</b-row>
</div>
</template>
<script>
export default {
name: 'GddStatus',
props: {
balance: { type: Number, default: 0 },
GdtBalance: { type: Number, default: 0 },
pending: {
type: Boolean,
default: true,
},
},
}
</script>

View File

@ -0,0 +1,135 @@
import { mount } from '@vue/test-utils'
import SendOverview from './SendOverview'
const sendMock = jest.fn()
sendMock.mockResolvedValue('success')
const localVue = global.localVue
// window.scrollTo = jest.fn()
describe('SendOverview', () => {
let wrapper
const propsData = {
balance: 123.45,
transactionCount: 1,
}
const mocks = {
$t: jest.fn((t) => t),
$n: jest.fn((n) => String(n)),
$store: {
state: {
email: 'sender@example.org',
},
},
$apollo: {
mutate: sendMock,
},
}
const Wrapper = () => {
return mount(SendOverview, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('has a status GDD line gdd-status-gdd', () => {
expect(wrapper.find('div.gdd-status-gdd').exists()).toBeTruthy()
})
it('has a send field', () => {
expect(wrapper.find('div.gdd-send').exists()).toBeTruthy()
})
// it('has a transactions table', () => {
// expect(wrapper.find('div.gdd-transaction-list').exists()).toBeTruthy()
// })
describe('transaction form', () => {
it('steps forward in the dialog', async () => {
await wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
})
expect(wrapper.findComponent({ name: 'TransactionConfirmation' }).exists()).toBeTruthy()
})
})
describe('confirm transaction', () => {
beforeEach(() => {
wrapper.setData({
currentTransactionStep: 1,
transactionData: {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
},
})
})
it('resets the transaction process when on-reset is emitted', async () => {
await wrapper.findComponent({ name: 'TransactionConfirmation' }).vm.$emit('on-reset')
expect(wrapper.findComponent({ name: 'TransactionForm' }).exists()).toBeTruthy()
expect(wrapper.vm.transactionData).toEqual({
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
})
})
describe('transaction is confirmed and server response is success', () => {
beforeEach(async () => {
jest.clearAllMocks()
await wrapper
.findComponent({ name: 'TransactionConfirmation' })
.vm.$emit('send-transaction')
})
it('calls the API when send-transaction is emitted', async () => {
expect(sendMock).toBeCalledWith(
expect.objectContaining({
variables: {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
},
}),
)
})
it('emits update-balance', () => {
expect(wrapper.emitted('update-balance')).toBeTruthy()
expect(wrapper.emitted('update-balance')).toEqual([[23.45]])
})
it('shows the succes page', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_success')
})
})
describe('transaction is confirmed and server response is error', () => {
beforeEach(async () => {
jest.clearAllMocks()
sendMock.mockRejectedValue({ message: 'receiver not found' })
await wrapper
.findComponent({ name: 'TransactionConfirmation' })
.vm.$emit('send-transaction')
})
it('shows the error page', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_error')
})
it('shows recipient not found', () => {
expect(wrapper.text()).toContain('transaction.receiverNotFound')
})
})
})
})
})

View File

@ -0,0 +1,122 @@
<template>
<div>
<b-container fluid>
<b-row>
<b-col class="bg-gray text-white text-center p-3">
<status
class="gdd-status-gdd"
v-if="showContext"
:pending="pending"
:balance="balance"
status-text="GDD"
/>
</b-col>
</b-row>
<br />
<gdd-send :currentTransactionStep="currentTransactionStep">
<template #transaction-form>
<transaction-form :balance="balance" @set-transaction="setTransaction"></transaction-form>
</template>
<template #transaction-confirmation>
<transaction-confirmation
:email="transactionData.email"
:amount="transactionData.amount"
:memo="transactionData.memo"
:loading="loading"
@send-transaction="sendTransaction"
@on-reset="onReset"
></transaction-confirmation>
</template>
<template #transaction-result>
<transaction-result
:error="error"
:errorResult="errorResult"
@on-reset="onReset"
></transaction-result>
</template>
</gdd-send>
<hr />
</b-container>
</div>
</template>
<script>
import Status from '../../components/Status.vue'
import GddSend from './SendOverview/GddSend.vue'
import TransactionForm from './SendOverview/GddSend/TransactionForm.vue'
import TransactionConfirmation from './SendOverview/GddSend/TransactionConfirmation.vue'
import TransactionResult from './SendOverview/GddSend/TransactionResult.vue'
import { sendCoins } from '../../graphql/mutations.js'
const EMPTY_TRANSACTION_DATA = {
email: '',
amount: 0,
memo: '',
}
export default {
name: 'SendOverview',
components: {
Status,
GddSend,
TransactionForm,
TransactionConfirmation,
TransactionResult,
},
data() {
return {
transactionData: { ...EMPTY_TRANSACTION_DATA },
error: false,
errorResult: '',
currentTransactionStep: 0,
loading: false,
}
},
props: {
balance: { type: Number, default: 0 },
GdtBalance: { type: Number, default: 0 },
transactions: {
default: () => [],
},
pending: {
type: Boolean,
default: true,
},
},
computed: {
showContext() {
return this.currentTransactionStep === 0
},
},
methods: {
setTransaction(data) {
this.transactionData = { ...data }
this.currentTransactionStep = 1
},
async sendTransaction() {
this.loading = true
this.$apollo
.mutate({
mutation: sendCoins,
variables: this.transactionData,
})
.then(() => {
this.error = false
this.$emit('update-balance', this.transactionData.amount)
})
.catch((err) => {
this.errorResult = err.message
this.error = true
})
this.currentTransactionStep = 2
this.loading = false
},
onReset() {
this.currentTransactionStep = 0
},
},
}
</script>

View File

@ -1735,13 +1735,6 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/chart.js@^2.7.55":
version "2.9.30"
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.30.tgz#34b99897f4f5ef0f74c8fe4ced70ac52b4d752dd"
integrity sha512-EgjxUUZFvf6ls3kW2CwyrnSJhgyKxgwrlp/W5G9wqyPEO9iFatO63zAA7L24YqgMxiDjQ+tG7ODU+2yWH91lPg==
dependencies:
moment "^2.10.2"
"@types/d3@3.5.38":
version "3.5.38"
resolved "https://registry.yarnpkg.com/@types/d3/-/d3-3.5.38.tgz#76f8f2e9159ae562965b2fa0e6fbee1aa643a1bc"
@ -3823,29 +3816,6 @@ chardet@^0.4.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=
chart.js@^2.9.3:
version "2.9.4"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684"
integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
dependencies:
chartjs-color "^2.1.0"
moment "^2.10.2"
chartjs-color-string@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71"
integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==
dependencies:
color-name "^1.0.0"
chartjs-color@^2.1.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.4.1.tgz#6118bba202fe1ea79dd7f7c0f9da93467296c3b0"
integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==
dependencies:
chartjs-color-string "^0.6.0"
color-convert "^1.9.3"
check-types@^8.0.3:
version "8.0.3"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
@ -4073,7 +4043,7 @@ collection-visit@^1.0.0:
map-visit "^1.0.0"
object-visit "^1.0.0"
color-convert@^1.9.0, color-convert@^1.9.1, color-convert@^1.9.3:
color-convert@^1.9.0, color-convert@^1.9.1:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
@ -9620,7 +9590,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
moment@^2.10.2, moment@^2.19.2:
moment@^2.19.2:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
@ -13469,13 +13439,6 @@ vue-bootstrap-typeahead@^0.2.6:
resize-observer-polyfill "^1.5.0"
vue "^2.5.17"
vue-chartjs@^3.5.0:
version "3.5.1"
resolved "https://registry.yarnpkg.com/vue-chartjs/-/vue-chartjs-3.5.1.tgz#d25e845708f7744ae51bed9d23a975f5f8fc6529"
integrity sha512-foocQbJ7FtveICxb4EV5QuVpo6d8CmZFmAopBppDIGKY+esJV8IJgwmEW0RexQhxqXaL/E1xNURsgFFYyKzS/g==
dependencies:
"@types/chart.js" "^2.7.55"
vue-cli-plugin-i18n@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vue-cli-plugin-i18n/-/vue-cli-plugin-i18n-1.0.1.tgz#5a3077de5d62c9b4068e486db1fc97fce9fa0072"