add visualize federation

This commit is contained in:
ogerly 2023-03-29 10:57:31 +02:00
parent 47fe739c77
commit a1b8be4329
13 changed files with 280 additions and 5 deletions

View File

@ -33,6 +33,7 @@
"bootstrap": "4.3.1",
"bootstrap-vue": "^2.21.2",
"core-js": "^3.6.5",
"date-fns": "^2.29.3",
"dotenv-webpack": "^7.0.3",
"express": "^4.17.1",
"graphql": "^15.6.1",

View File

@ -0,0 +1,76 @@
<template>
<div class="federation-visualize-item">
<b-row>
<b-col cols="1"><b-icon :icon="icon" :variant="variant" class="mr-4"></b-icon></b-col>
<b-col>
<div>{{ item.url }}</div>
<small>{{ `${item.publicKey.substring(0, 26)}` }}</small>
</b-col>
<b-col cols="2">{{ lastAnnouncedAt }}</b-col>
<b-col cols="2">{{ createdAt }}</b-col>
</b-row>
</div>
</template>
<script>
import { formatDistanceToNow } from 'date-fns'
import { de, en, fr, es, nl } from 'date-fns/locale'
export default {
name: 'FederationVisualizeItem',
props: {
item: { type: Object },
},
data() {
return {
oldPublicKey: '',
formatDistanceToNow,
locale: this.$i18n.locale,
}
},
computed: {
verified() {
return new Date(this.item.verifiedAt) >= new Date(this.item.lastAnnouncedAt)
},
icon() {
return this.verified ? 'check' : 'x-circle'
},
variant() {
return this.verified ? 'success' : 'danger'
},
fnsLocale() {
switch (this.locale) {
case 'de':
return de
case 'es':
return es
case 'fr':
return fr
case 'nl':
return nl
default:
return en
}
},
lastAnnouncedAt() {
if (this.item.lastAnnouncedAt) {
return formatDistanceToNow(new Date(this.item.lastAnnouncedAt), {
includeSecond: true,
addSuffix: true,
locale: this.fnsLocale,
})
}
return ''
},
createdAt() {
if (this.item.createdAt) {
return formatDistanceToNow(new Date(this.item.createdAt), {
includeSecond: true,
addSuffix: true,
locale: this.fnsLocale,
})
}
return ''
},
},
}
</script>

View File

@ -0,0 +1,44 @@
import { mount } from '@vue/test-utils'
import FederationVisualizeItem from './FederationVisualizeItem.vue'
const localVue = global.localVue
const mocks = {
$t: (key) => key,
$i18n: {
locale: 'de',
t: (key) => key,
},
}
describe('FederationVisualizeItem', () => {
let wrapper
const propsData = {
item: {
id: 7590,
foreign: false,
publicKey: 'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7',
url: 'http://localhost/api/api/2_0',
lastAnnouncedAt: null,
verifiedAt: null,
lastErrorAt: null,
createdAt: '2023-03-29T04:46:38.823Z',
updatedAt: null,
},
}
const Wrapper = () => {
return mount(FederationVisualizeItem, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('renders the component', () => {
expect(wrapper.find('div.federation-visualize-item').exists()).toBe(true)
})
})
})

View File

@ -62,8 +62,12 @@ describe('NavBar', () => {
)
})
it('has a link to /federation', () => {
expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe('/federation')
})
it('has a link to /statistic', () => {
expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe('/statistic')
expect(wrapper.findAll('.nav-item').at(4).find('a').attributes('href')).toBe('/statistic')
})
})
@ -72,7 +76,7 @@ describe('NavBar', () => {
beforeEach(async () => {
delete window.location
window.location = ''
await wrapper.findAll('.nav-item').at(4).find('a').trigger('click')
await wrapper.findAll('.nav-item').at(5).find('a').trigger('click')
})
afterEach(() => {
@ -97,7 +101,7 @@ describe('NavBar', () => {
window.location = {
assign: windowLocationMock,
}
await wrapper.findAll('.nav-item').at(5).find('a').trigger('click')
await wrapper.findAll('.nav-item').at(6).find('a').trigger('click')
})
afterEach(() => {

View File

@ -1,6 +1,6 @@
<template>
<div class="component-nabvar">
<b-navbar toggleable="md" type="dark" variant="success">
<b-navbar toggleable="lg" type="dark" class="bg-dark">
<b-navbar-brand class="mb-2" to="/">
<img src="img/brand/gradido_logo_w.png" class="navbar-brand-img pl-2" alt="..." />
</b-navbar-brand>
@ -19,6 +19,9 @@
<b-nav-item to="/contribution-links">
{{ $t('navbar.automaticContributions') }}
</b-nav-item>
<b-nav-item to="/federation">
{{ $t('navbar.instances') }}
</b-nav-item>
<b-nav-item to="/statistic">{{ $t('navbar.statistic') }}</b-nav-item>
<b-nav-item @click="wallet">{{ $t('navbar.my-account') }}</b-nav-item>
<b-nav-item @click="logout">{{ $t('navbar.logout') }}</b-nav-item>

View File

@ -0,0 +1,17 @@
import gql from 'graphql-tag'
export const getCommunities = gql`
query {
getCommunities {
id
foreign
publicKey
url
lastAnnouncedAt
verifiedAt
lastErrorAt
createdAt
updatedAt
}
}
`

View File

@ -76,6 +76,13 @@
},
"short_hash": "({shortHash})"
},
"federation": {
"gradidoInstances":"Gradido Instanzen",
"verified":"Verifiziert",
"url":"Url",
"lastAnnouncedAt":"letzte Bekanntgabe",
"createdAt":"Erstellt am"
},
"form": {
"cancel": "Abbrechen",
"submit": "Senden"
@ -104,6 +111,7 @@
"name": "Name",
"navbar": {
"automaticContributions": "Automatische Beiträge",
"instances":"Instanzen",
"logout": "Abmelden",
"my-account": "Mein Konto",
"statistic": "Statistik",

View File

@ -76,6 +76,13 @@
},
"short_hash": "({shortHash})"
},
"federation": {
"gradidoInstances":"Gradido Instances",
"verified":"Verified",
"url":"Url",
"lastAnnouncedAt":"Last Announced",
"createdAt":"Created At "
},
"form": {
"cancel": "Cancel",
"submit": "Send"
@ -104,6 +111,7 @@
"name": "Name",
"navbar": {
"automaticContributions": "Automatic Contributions",
"instances":"Instances",
"logout": "Logout",
"my-account": "My Account",
"statistic": "Statistic",

View File

@ -0,0 +1,30 @@
import { mount } from '@vue/test-utils'
import FederationVisualize from './FederationVisualize'
const localVue = global.localVue
const mocks = {
$t: (key) => key,
$i18n: {
locale: 'de',
t: (key) => key,
},
}
describe('Overview', () => {
let wrapper
const Wrapper = () => {
return mount(FederationVisualize, { localVue, mocks })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('has a DIV element with the class.component-confirm-register-mail', () => {
expect(wrapper.find('div.federation-visualize').exists()).toBe(true)
})
})
})

View File

@ -0,0 +1,68 @@
<template>
<div class="federation-visualize">
<div class="d-flex justify-content-between align-items-center mb-3">
<span class="h2">{{ $t('federation.gradidoInstances') }}</span>
<b-button>
<b-icon
icon="arrow-clockwise"
font-scale="2"
:animation="animation"
@click="$apollo.queries.GetCommunities.refresh()"
></b-icon>
</b-button>
</div>
<b-list-group>
<b-row>
<b-col cols="1" class="ml-1">{{ $t('federation.verified') }}</b-col>
<b-col class="ml-3">{{ $t('federation.url') }}</b-col>
<b-col cols="2">{{ $t('federation.lastAnnouncedAt') }}</b-col>
<b-col cols="2">{{ $t('federation.createdAt') }}</b-col>
</b-row>
<b-list-group-item
v-for="item in communities"
:key="item.id"
:variant="!item.foreign ? 'primary' : 'warning'"
>
<federation-visualize-item :item="item" />
</b-list-group-item>
</b-list-group>
</div>
</template>
<script>
import { getCommunities } from '@/graphql/getCommunities'
import FederationVisualizeItem from '../components/Fedaration/FederationVisualizeItem.vue'
export default {
name: 'FederationVisualize',
components: {
FederationVisualizeItem,
},
data() {
return {
oldPublicKey: '',
communities: [],
icon: '',
animation: '',
}
},
apollo: {
GetCommunities: {
fetchPolicy: 'network-only',
query() {
this.animation = 'spin'
return getCommunities
},
update({ getCommunities }) {
this.communities = getCommunities
this.animation = ''
},
error({ message }) {
this.toastError(message)
this.animation = ''
},
},
},
}
</script>

View File

@ -45,7 +45,7 @@ describe('router', () => {
describe('routes', () => {
it('has nine routes defined', () => {
expect(routes).toHaveLength(8)
expect(routes).toHaveLength(9)
})
it('has "/overview" as default', async () => {
@ -88,6 +88,13 @@ describe('router', () => {
})
})
describe('federation', () => {
it('loads the "FederationVisualize" page', async () => {
const component = await routes.find((r) => r.path === '/federation').component()
expect(component.default.name).toBe('FederationVisualize')
})
})
describe('not found page', () => {
it('renders the "NotFound" component', async () => {
const component = await routes.find((r) => r.path === '*').component()

View File

@ -31,6 +31,10 @@ const routes = [
path: '*',
component: () => import('@/components/NotFoundPage.vue'),
},
{
path: '/federation',
component: () => import('@/pages/FederationVisualize.vue'),
},
]
export default routes

View File

@ -5038,6 +5038,11 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
date-fns@^2.29.3:
version "2.29.3"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"