Merge branch 'master' into login_group_add_host

This commit is contained in:
einhornimmond 2021-05-03 12:01:02 +02:00
commit d6420a990b
40 changed files with 401 additions and 358 deletions

View File

@ -5,21 +5,5 @@ labels: bug
title: 🐛 [Bug]
---
## :bug: Bugreport
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the bug is.-->
### Steps to reproduce the behavior
1.
2.
3.
4. ...
5. Profit
### Expected behavior
<!-- A clear and concise description of what you expected to happen. -->
### Version & Environment
<!-- Add context about your environment and used version here. -->
### Additional context
<!-- Add any other context about the problem here. -->
## 🐛 Bugreport
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the bug is.--

View File

@ -7,18 +7,3 @@ title: 💥 [DevOps]
## 💥 DevOps ticket
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the problem is.-->
### Motive
<!-- Why does this task need to be done? What can we benefit from this? -->
### Related issues
<!-- Are there any related issues to link to? Please paste them below for reference. -->
### Implementation
<!-- Please, document any ideas of how the task can be performed. -->
### Validation
<!-- How can we make sure that this task was successful? -->
### Additional context
<!-- Add other context or background about the feature request here.-->

View File

@ -5,20 +5,5 @@ labels: feature
title: 🚀 [Feature]
---
## :rocket: Feature
## 🚀 Feature
<!-- Give a short summary of the Feature. Use Screenshots if you want. -->
### User Problem
<!-- Which problem is this solving? Why do you think this is important? Who will benefit from it and how? -->
### Implementation
<!-- How do you think this feature should be implemented? How will it be used? Where in the network should it be located? Which steps and screens are involved? -->
### Design & Layout
<!-- Attach Screenshots and Sketches to illustrate your idea. -->
### Validation
<!-- How can we make sure that this feature indeed solves the above problem? How do we know if it has been accepted by the users of the network, once released? -->
### Additional context
<!-- Add other context or background about the feature request here.-->

View File

@ -1,11 +1,11 @@
---
name: 💬 Question
about: If you need help understanding HumanConnection.
about: If you need help understanding Gradido.
labels: question
title: 💬 [Question]
---
<!-- Chat with Team Gradido -->
<!-- If you need an answer right away, visit the Gradido Discord: https://discord.gg/kA3zBAKQDC -->
## :speech_balloon: Question
## 💬 Question
<!-- Describe your Question in detail. Include screenshots and drawings if needed. -->

View File

@ -5,17 +5,5 @@ labels: refactor
title: 🔧 [Refactor]
---
## :zap: Refactor ticket
## 🔧 Refactor ticket
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the problem is.-->
### Motive
<!-- What is the purpose of this refactoring? If it's removing depcrecated code, please link to the deprecation notice. -->
### Related issues
<!-- Are there any related issues to link to? Please paste them below for reference. -->
### Implementation
<!-- Please, document any ideas of how the code should be refactored. -->
### Additional context
<!-- Add other context or background about the feature request here.-->

View File

@ -212,7 +212,7 @@ jobs:
report_name: Coverage Frontend
type: lcov
result_path: ./coverage/lcov.info
min_coverage: 13
min_coverage: 15
token: ${{ github.token }}
#test:

View File

@ -167,13 +167,15 @@ class AppController extends Controller
$php_session_id = intval($session->read('session_id'));
}
$cookie_session_id = intval($this->request->getCookie('GRADIDO_LOGIN', ''));
if($php_session_id != 0) {
// decide in which order session_ids are tried
if($sessionId != 0) {
$session_id = $sessionId;
} else if($php_session_id != 0) {
$session_id = $php_session_id;
} else if($cookie_session_id != 0) {
$session_id = $cookie_session_id;
} else {
$session_id = $sessionId;
}
}
$ip = $this->request->clientIp();
if (!$session->check('client_ip')) {
$session->write('client_ip', $ip);

View File

@ -61,8 +61,8 @@ class AppRequestsController extends AppController
if(is_array($field)) {
$one_exist = false;
foreach($field as $oneField) {
if(isset($data[$oneField])) {
$param[$oneField] = $data[$oneField];
if(isset($data->$oneField)) {
$param[$oneField] = $data->$oneField;
$one_exist = true;
break;
}
@ -71,10 +71,10 @@ class AppRequestsController extends AppController
return ['state' => 'error', 'msg' => 'missing field of set', 'details' => $field];
}
} else {
if(!isset($data[$field])) {
if(!isset($data->$field)) {
return ['state' => 'error', 'msg' => 'missing field', 'details' => $field . ' not found'];
} else {
$param[$field] = $data[$field];
$param[$field] = $data->$field;
}
}
}
@ -85,9 +85,9 @@ class AppRequestsController extends AppController
{
foreach(array_keys($replaceKeys) as $key) {
$newKey = $replaceKeys[$key];
if(isset($data[$key])) {
$data[$newKey] = $data[$key];
unset($data[$key]);
if(isset($data->$key)) {
$data->$newKey = $data->$key;
unset($data->$key);
}
}
}
@ -98,8 +98,8 @@ class AppRequestsController extends AppController
$data = $this->request->input('json_decode');
}
$session_id = 0;
if(isset($data['session_id'])) {
$session_id = $data['session_id'];
if(isset($data->session_id)) {
$session_id = $data->session_id;
}
$login_request_result = $this->requestLogin($session_id, false);
if($login_request_result !== true) {
@ -119,12 +119,12 @@ class AppRequestsController extends AppController
return ['state' => 'error', 'msg' => 'amount is invalid', 'details' => $param['amount']];
}
if(isset($data['memo'])) {
$param['memo'] = $data['memo'];
if(isset($data->memo)) {
$param['memo'] = $data->memo;
}
if(isset($data['auto_sign'])) {
$param['auto_sign'] = boolval($data['auto_sign']);
if(isset($data->auto_sign)) {
$param['auto_sign'] = boolval($data->auto_sign);
}
return true;

View File

@ -199,7 +199,7 @@ class StateBalancesTable extends Table
$amount = 0;
if($transaction->transaction_type_id == 1) { // creation
$temp = $transaction->transaction_creations[0];
$temp = $transaction->transaction_creation;
/*$balance_temp = $this->newEntity();
$balance_temp->amount = $temp->amount;
@ -211,7 +211,7 @@ class StateBalancesTable extends Table
//$amount_date =
} else if($transaction->transaction_type_id == 2) { // transfer
$temp = $transaction->transaction_send_coins[0];
$temp = $transaction->transaction_send_coin;
$amount = intval($temp->amount);
// reverse if sender
if($stateUserId == $temp->state_user_id) {

View File

@ -8,6 +8,8 @@ services:
image: gradido/frontend:development
build:
target: development
networks:
- external-net
environment:
- NODE_ENV="development"
# - DEBUG=true

View File

@ -15,13 +15,13 @@ services:
context: ./frontend
target: production
networks:
- external-net
- internal-net
ports:
- 8080:8080
- 3000:3000
environment:
# Envs used in Dockerfile
# - DOCKER_WORKDIR="/app"
# - PORT="8080"
# - PORT=3000
- BUILD_DATE
- BUILD_VERSION
- BUILD_COMMIT

View File

@ -1,3 +1,3 @@
LOGIN_API_URL=http://localhost/login_api/
COMMUNITY_API_URL=http://localhost/api/
VUE_PATH=/vue
ALLOW_REGISTER=true

View File

@ -15,7 +15,7 @@ ENV BUILD_COMMIT="0000000"
## SET NODE_ENV
ENV NODE_ENV="production"
## App relevant Envs
ENV PORT="8080"
ENV PORT="3000"
# Labels
LABEL org.label-schema.build-date="${BUILD_DATE}"
@ -82,15 +82,14 @@ FROM base as production
# Copy "binary"-files from build image
COPY --from=build ${DOCKER_WORKDIR}/dist ./dist
#COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
#COPY --from=build ${DOCKER_WORKDIR}/nuxt.config.js ./nuxt.config.js
# We also copy the node_modules express and serve-static for the run script
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
# Copy static files
# TODO - this should be one Folder containign all stuff needed to be copied
#COPY --from=build ${DOCKER_WORKDIR}/constants ./constants
#COPY --from=build ${DOCKER_WORKDIR}/static ./static
#COPY --from=build ${DOCKER_WORKDIR}/locales ./locales
COPY --from=build ${DOCKER_WORKDIR}/public ./public
# Copy package.json for script definitions (lock file should not be needed)
COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json
# Copy run scripts run/
COPY --from=build ${DOCKER_WORKDIR}/run ./run
# Run command
CMD /bin/sh -c "yarn run start"

View File

@ -3,7 +3,7 @@
"version": "0.9.4",
"private": true,
"scripts": {
"start": "node server.js",
"start": "node run/server.js",
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "eslint --ext .js,.vue .",
@ -69,6 +69,7 @@
"vue-good-table": "^2.21.3",
"vue-i18n": "^8.22.4",
"vue-jest": "^3.0.7",
"vue-loading-overlay": "^3.4.2",
"vue-moment": "^4.1.0",
"vue-qrcode": "^0.3.5",
"vue-qrcode-reader": "^2.3.16",

14
frontend/run/server.js Normal file
View File

@ -0,0 +1,14 @@
// Imports
const express = require('express')
const serveStatic = require('serve-static')
// Port
const port = process.env.PORT || 3000
// Express Server
const app = express()
app.use(serveStatic(__dirname + '/../dist'))
app.listen(port)
// eslint-disable-next-line no-console
console.log(`http://frontend:${port} server started.`)

View File

@ -1,7 +0,0 @@
var express = require('express')
var serveStatic = require('serve-static')
var app = express()
app.use(serveStatic(__dirname + '/dist'))
var port = process.env.PORT || 5000
app.listen(port)
// console.log('http://localhost:5000 server started.');

View File

@ -49,12 +49,13 @@ const communityAPI = {
}
return apiPost(CONFIG.COMMUNITY_API__URL + 'createCoins/', payload)
},*/
send: async (session_id, email, amount, memo) => {
send: async (session_id, email, amount, memo, target_date) => {
const payload = {
session_id,
email,
amount,
memo,
target_date,
auto_sign: true,
}
return apiPost(CONFIG.COMMUNITY_API_URL + 'sendCoins/', payload)

View File

@ -10,7 +10,7 @@
<img :src="logo" class="navbar-brand-img" alt="..." />
</div>
<b-row class="text-center">
<b-col>{{ $n($store.state.user.balance) }} GDD</b-col>
<b-col>{{ $n(balance) }} GDD</b-col>
</b-row>
<slot name="mobile-right">
<ul class="nav align-items-center d-md-none">
@ -88,6 +88,10 @@ export default {
default: true,
description: 'Whether sidebar should autoclose on mobile when clicking an item',
},
balance: {
type: Number,
default: 0,
},
},
provide() {
return {

View File

@ -2,11 +2,11 @@
// Load Package Details for some default values
// const pkg = require('../../package')
const environment = {
NODE_ENV: process.env.NODE_ENV,
DEBUG: process.env.NODE_ENV !== 'production' || false,
PRODUCTION: process.env.NODE_ENV === 'production' || false,
ALLOW_REGISTER: process.env.ALLOW_REGISTER !== 'false',
}
const server = {

View File

@ -37,6 +37,10 @@ import 'vue-good-table/dist/vue-good-table.css'
import VueMoment from 'vue-moment'
import Loading from 'vue-loading-overlay'
// import the styles
import 'vue-loading-overlay/dist/vue-loading.css'
Object.keys(rules).forEach((rule) => {
extend(rule, {
...rules[rule], // copies rule configuration
@ -56,6 +60,7 @@ export default {
Vue.use(VueQrcodeReader)
Vue.use(VueQrcode)
Vue.use(VueFlatPickr)
Vue.use(Loading)
configure({
classes: {
valid: 'is-valid',

View File

@ -1,11 +1,13 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
import CONFIG from '../config'
Vue.use(VueRouter)
// configure router
const router = new VueRouter({
base: '/vue',
routes, // short for routes: routes
linkActiveClass: 'active',
mode: 'history',
@ -20,4 +22,11 @@ const router = new VueRouter({
},
})
if (CONFIG.ALLOW_REGISTER) {
router.addRoute({
path: '/register',
component: () => import('../views/Pages/Register.vue'),
})
}
export default router

View File

@ -21,20 +21,20 @@ const routes = [
requiresAuth: true,
},
},
{
path: '/profileedit',
component: () => import('../views/Pages/UserProfileEdit.vue'),
meta: {
requiresAuth: true,
},
},
{
path: '/activity',
component: () => import('../views/Pages/UserProfileActivity.vue'),
meta: {
requiresAuth: true,
},
},
//{
// path: '/profileedit',
// component: () => import('../views/Pages/UserProfileEdit.vue'),
// meta: {
// requiresAuth: true,
// },
//},
//{
// path: '/activity',
// component: () => import('../views/Pages/UserProfileActivity.vue'),
// meta: {
// requiresAuth: true,
// },
//},
{
path: '/transactions',
component: () => import('../views/Pages/UserProfileTransactionList.vue'),
@ -50,10 +50,6 @@ const routes = [
path: '/thx',
component: () => import('../views/Pages/thx.vue'),
},
{
path: '/register',
component: () => import('../views/Pages/Register.vue'),
},
{
path: '/password',
component: () => import('../views/Pages/ForgotPassword.vue'),

View File

@ -1,9 +1,32 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import communityAPI from '../apis/communityAPI'
import createPersistedState from 'vuex-persistedstate'
export const mutations = {
language: (state, language) => {
state.language = language
},
email: (state, email) => {
state.email = email
},
session_id: (state, session_id) => {
state.session_id = session_id
},
}
export const actions = {
login: ({ dispatch, commit }, data) => {
commit('session_id', data.session_id)
commit('email', data.email)
},
logout: ({ commit, state }) => {
commit('session_id', null)
commit('email', null)
sessionStorage.clear()
},
}
export const store = new Vuex.Store({
plugins: [
createPersistedState({
@ -14,64 +37,10 @@ export const store = new Vuex.Store({
session_id: null,
email: '',
language: 'en',
user: {
name: '',
balance: 0,
balance_gdt: 0,
},
modals: false,
optionAxios: {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true',
},
},
},
getters: {},
// Syncronous mutation of the state
mutations: {
language: (state, language) => {
state.language = language
},
email: (state, email) => {
state.email = email
},
session_id: (state, session_id) => {
state.session_id = session_id
},
user_balance: (state, balance) => {
state.user.balance = balance / 10000
},
user_balance_gdt: (state, balance) => {
state.user.balance_gdt = balance / 10000
},
},
actions: {
login: ({ dispatch, commit }, data) => {
commit('session_id', data.session_id)
commit('email', data.email)
},
passwordReset: (data) => {},
schoepfen: (data) => {
// http://localhost/transaction-creations/ajaxCreate
},
createUser: ({ commit, dispatch }, data) => {
commit('session_id', data.session_id)
commit('email', data.email)
},
logout: ({ commit, state }) => {
commit('session_id', null)
commit('email', null)
sessionStorage.clear()
},
accountBalance: async ({ commit, dispatch, state }) => {
const result = await communityAPI.balance(state.session_id)
if (result.success) {
commit('user_balance', result.result.data.balance)
} else {
//dispatch('logout')
}
},
},
mutations,
actions,
})

View File

@ -0,0 +1,81 @@
import { mutations, actions } from './store'
const { language, email, session_id } = mutations
const { login, logout } = actions
describe('Vuex store', () => {
describe('mutations', () => {
describe('language', () => {
it('sets the state of language', () => {
const state = { language: 'en' }
language(state, 'de')
expect(state.language).toEqual('de')
})
})
describe('email', () => {
it('sets the state of email', () => {
const state = { email: 'nobody@knows.tv' }
email(state, 'someone@there.is')
expect(state.email).toEqual('someone@there.is')
})
})
describe('session_id', () => {
it('sets the state of session_id', () => {
const state = { session_id: null }
session_id(state, '1234')
expect(state.session_id).toEqual('1234')
})
})
})
describe('actions', () => {
describe('login', () => {
const commit = jest.fn()
const state = {}
it('calls two commits', () => {
login({ commit, state }, { session_id: 1234, email: 'someone@there.is' })
expect(commit).toHaveBeenCalledTimes(2)
})
it('commits session_id', () => {
login({ commit, state }, { session_id: 1234, email: 'someone@there.is' })
expect(commit).toHaveBeenNthCalledWith(1, 'session_id', 1234)
})
it('commits email', () => {
login({ commit, state }, { session_id: 1234, email: 'someone@there.is' })
expect(commit).toHaveBeenNthCalledWith(2, 'email', 'someone@there.is')
})
})
describe('logout', () => {
const commit = jest.fn()
const state = {}
it('calls two commits', () => {
logout({ commit, state })
expect(commit).toHaveBeenCalledTimes(2)
})
it('commits session_id', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(1, 'session_id', null)
})
it('commits email', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(2, 'email', null)
})
// how can I get this working?
it.skip('calls sessionStorage.clear()', () => {
logout({ commit, state })
const spy = jest.spyOn(sessionStorage, 'clear')
expect(spy).toHaveBeenCalledTimes(1)
})
})
})
})

View File

@ -1,6 +1,6 @@
<template>
<div>
<b-row v-show="row_form">
<b-row v-show="showTransactionList">
<b-col xl="12" md="12">
<b-alert show dismissible variant="warning" class="text-center">
<span class="alert-text" v-html="$t('form.attention')"></span>
@ -10,14 +10,14 @@
<span class="alert-text" v-html="$t('form.scann_code')"></span>
<b-col v-show="!scan" lg="12" class="text-right">
<a @click="toggle" class="nav-link pointer">
<img src="/img/icons/gradido/qr-scan-pure.png" height="50" />
<img src="img/icons/gradido/qr-scan-pure.png" height="50" />
</a>
</b-col>
<div v-if="scan">
<!-- <b-row>
<qrcode-capture @detect="onDetect" capture="user" ></qrcode-capture>
</b-row> -->
<qrcode-capture @detect="onDetect" capture="user" ></qrcode-capture>
</b-row> -->
<qrcode-stream class="mt-3" @decode="onDecode" @detect="onDetect"></qrcode-stream>
@ -80,7 +80,7 @@
<br />
<div>
<b-col class="text-left p-3 p-sm-1">{{ $t('form.amount') }}</b-col>
<b-col v-if="$store.state.user.balance == form.amount" class="text-right">
<b-col v-if="balance == form.amount" class="text-right">
<b-badge variant="primary">{{ $t('form.max_gdd_info') }}</b-badge>
</b-col>
<b-input-group
@ -100,7 +100,7 @@
placeholder="0.01"
step="0.01"
min="0.01"
:max="$store.state.user.balance"
:max="balance"
style="font-size: xx-large; padding-left: 20px"
></b-form-input>
</b-input-group>
@ -204,9 +204,8 @@ export default {
BIcon,
},
props: {
row_form: { type: Boolean, default: true },
row_check: { type: Boolean, default: false },
row_thx: { type: Boolean, default: false },
balance: { type: Number, default: 0 },
showTransactionList: { type: Boolean, default: true },
},
data() {
return {
@ -226,6 +225,8 @@ export default {
auto_sign: true,
},
send: false,
row_check: false,
row_thx: false,
}
},
computed: {},
@ -234,7 +235,6 @@ export default {
this.scan = !this.scan
},
async onDecode(decodedString) {
//console.log('onDecode JSON.parse(decodedString)', JSON.parse(decodedString))
const arr = JSON.parse(decodedString)
this.form.email = arr[0].email
this.form.amount = arr[0].amount
@ -244,30 +244,31 @@ export default {
//event.preventDefault()
this.ajaxCreateData.email = this.form.email
this.ajaxCreateData.amount = this.form.amount
this.ajaxCreateData.target_date = Date.now()
const now = new Date(Date.now()).toISOString()
this.ajaxCreateData.target_date = now
this.ajaxCreateData.memo = this.form.memo
this.$emit('change-rows', { row_form: false, row_check: true, row_thx: false })
this.$emit('toggle-show-list', false)
this.row_check = true
this.row_thx = false
},
async sendTransaction() {
this.ajaxCreateData.amount = this.ajaxCreateData.amount * 10000
const result = await communityAPI.send(
this.$store.state.session_id,
this.ajaxCreateData.email,
this.ajaxCreateData.amount,
this.ajaxCreateData.amount * 10000,
this.ajaxCreateData.memo,
this.ajaxCreateData.target_date,
)
// console.log(result)
if (result.success) {
// console.log('send success')
this.$emit('change-rows', { row_form: false, row_check: false, row_thx: true })
this.$emit('toggle-show-list', false)
this.row_check = false
this.row_thx = true
this.$emit('update-balance', { ammount: this.ajaxCreateData.amount })
} else {
// console.log('send error')
alert('error')
this.$emit('change-rows', { row_form: true, row_check: false, row_thx: false })
this.$emit('toggle-show-list', true)
this.row_check = false
this.row_thx = false
}
},
onReset(event) {
@ -275,10 +276,12 @@ export default {
this.form.email = ''
this.form.amount = ''
this.show = false
this.$emit('toggle-show-list', true)
this.row_check = false
this.row_thx = false
this.$nextTick(() => {
this.show = true
})
this.$emit('change-rows', { row_form: true, row_check: false, row_thx: false })
},
},
}

View File

@ -1,29 +1,22 @@
import { mount } from '@vue/test-utils'
import GddStatus from './GddStatus'
import Vuex from 'vuex'
const localVue = global.localVue
describe('GddStatus', () => {
let wrapper
let state = {
user: {
balance: 1234,
balance_gdt: 9876,
},
}
let store = new Vuex.Store({
state,
})
let mocks = {
$n: jest.fn((n) => n),
}
let propsData = {
balance: 1234,
GdtBalance: 9876,
}
const Wrapper = () => {
return mount(GddStatus, { localVue, store, mocks })
return mount(GddStatus, { localVue, mocks, propsData })
}
describe('mount', () => {

View File

@ -1,6 +1,6 @@
<template>
<div>
<b-row v-show="this.row_form">
<b-row v-show="showTransactionList">
<b-col xl="6" md="6">
<stats-card
type="gradient-red"
@ -8,7 +8,7 @@
class="mb-4 h1"
style="background-color: #ebebeba3 !important"
>
{{ $n($store.state.user.balance) }} GDD
{{ $n(balance) }} GDD
</stats-card>
</b-col>
<b-col xl="6" md="6">
@ -18,7 +18,7 @@
class="mb-4 h1"
style="background-color: #ebebeba3 !important"
>
{{ $n($store.state.user.balance_gdt) }} GDT
{{ $n(GdtBalance) }} GDT
</stats-card>
</b-col>
</b-row>
@ -29,7 +29,9 @@
export default {
name: 'GddStatus',
props: {
row_form: { type: Boolean, default: true },
showTransactionList: { type: Boolean, default: true },
balance: { type: Number, default: 0 },
GdtBalance: { type: Number, default: 0 },
},
}
</script>

View File

@ -1,6 +1,6 @@
<template>
<div>
<b-list-group v-show="this.row_form">
<b-list-group v-show="showTransactionList">
<b-list-group-item
v-for="item in filteredItems"
:key="item.id"
@ -93,7 +93,7 @@ import communityAPI from '../../apis/communityAPI'
export default {
name: 'GddTable',
props: {
row_form: { type: Boolean, default: true },
showTransactionList: { type: Boolean, default: true },
},
data() {
return {
@ -108,7 +108,6 @@ export default {
const result = await communityAPI.transactions(this.$store.state.session_id)
if (result.success) {
this.$store.state.user.balance_gdt = result.result.data.gdtSum
this.items = result.result.data.transactions
this.count = result.result.data.count
} else {

View File

@ -79,7 +79,7 @@ describe('DashboardLayoutGdd', () => {
})
it('has five items in the navbar', () => {
expect(navbar.findAll('ul > li')).toHaveLength(5)
expect(navbar.findAll('ul > li')).toHaveLength(3)
})
it('has first item "send" in navbar', () => {
@ -116,29 +116,29 @@ describe('DashboardLayoutGdd', () => {
expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/profile')
})
it('has fourth item "Settigs" in navbar', () => {
expect(navbar.findAll('ul > li').at(3).text()).toEqual('site.navbar.settings')
})
//it('has fourth item "Settigs" in navbar', () => {
// expect(navbar.findAll('ul > li').at(3).text()).toEqual('site.navbar.settings')
//})
//
//it.skip('has fourth item "Settings" linked to profileedit in navbar', async () => {
// navbar.findAll('ul > li > a').at(3).trigger('click')
// await flushPromises()
// await jest.runAllTimers()
// await flushPromises()
// expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/profileedit')
//})
it.skip('has fourth item "Settings" linked to profileedit in navbar', async () => {
navbar.findAll('ul > li > a').at(3).trigger('click')
await flushPromises()
await jest.runAllTimers()
await flushPromises()
expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/profileedit')
})
it('has fifth item "Activity" in navbar', () => {
expect(navbar.findAll('ul > li').at(4).text()).toEqual('site.navbar.activity')
})
it.skip('has fourth item "Activity" linked to activity in navbar', async () => {
navbar.findAll('ul > li > a').at(4).trigger('click')
await flushPromises()
await jest.runAllTimers()
await flushPromises()
expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/activity')
})
//it('has fifth item "Activity" in navbar', () => {
// expect(navbar.findAll('ul > li').at(4).text()).toEqual('site.navbar.activity')
//})
//
//it.skip('has fourth item "Activity" linked to activity in navbar', async () => {
// navbar.findAll('ul > li > a').at(4).trigger('click')
// await flushPromises()
// await jest.runAllTimers()
// await flushPromises()
// expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/activity')
//})
})
})
})

View File

@ -1,7 +1,7 @@
<template>
<div class="wrapper">
<notifications></notifications>
<side-bar @logout="logout">
<side-bar @logout="logout" :balance="balance">
<template slot="links">
<b-nav-item href="#!" to="/overview">
<b-nav-text class="p-0 text-lg text-muted">{{ $t('send') }}</b-nav-text>
@ -12,12 +12,14 @@
<b-nav-item href="#!" to="/profile">
<b-nav-text class="p-0 text-lg text-muted">{{ $t('site.navbar.my-profil') }}</b-nav-text>
</b-nav-item>
<!--
<b-nav-item href="#!" to="/profileedit">
<b-nav-text class="p-0 text-lg text-muted">{{ $t('site.navbar.settings') }}</b-nav-text>
</b-nav-item>
<b-nav-item href="#!" to="/activity">
<b-nav-text class="p-0 text-lg text-muted">{{ $t('site.navbar.activity') }}</b-nav-text>
</b-nav-item>
-->
</template>
</side-bar>
<div class="main-content">
@ -26,7 +28,11 @@
<div @click="$sidebar.displaySidebar(false)">
<fade-transition :duration="200" origin="center top" mode="out-in">
<!-- your content here -->
<router-view></router-view>
<router-view
:balance="balance"
:gdt-balance="GdtBalance"
@update-balance="updateBalance"
></router-view>
</fade-transition>
</div>
<content-footer v-if="!$route.meta.hideFooter"></content-footer>
@ -57,6 +63,7 @@ import DashboardNavbar from './DashboardNavbar.vue'
import ContentFooter from './ContentFooter.vue'
// import DashboardContent from './Content.vue';
import { FadeTransition } from 'vue2-transitions'
import communityAPI from '../../apis/communityAPI'
export default {
components: {
@ -65,6 +72,12 @@ export default {
// DashboardContent,
FadeTransition,
},
data() {
return {
balance: 0,
GdtBalance: 0,
}
},
methods: {
initScrollbar() {
let isWindows = navigator.platform.startsWith('Win')
@ -78,10 +91,33 @@ export default {
this.$store.dispatch('logout')
this.$router.push('/login')
},
async loadBalance() {
const result = await communityAPI.balance(this.$store.state.session_id)
if (result.success) {
this.balance = result.result.data.balance / 10000
} else {
// what to do when loading balance fails?
}
},
async loadGDTBalance() {
const result = await communityAPI.transactions(this.$store.state.session_id)
if (result.success) {
this.GdtBalance = result.result.data.gdtSum / 10000
} else {
// what to do when loading balance fails?
}
},
updateBalance(ammount) {
this.balance -= ammount
},
},
mounted() {
this.initScrollbar()
},
created() {
this.loadBalance()
this.loadGDTBalance()
},
}
</script>
<style lang="scss"></style>

View File

@ -1,26 +1,17 @@
import { shallowMount } from '@vue/test-utils'
import KontoOverview from './KontoOverview'
import Vuex from 'vuex'
const localVue = global.localVue
describe('KontoOverview', () => {
let wrapper
let actions = {
accountBalance: jest.fn(),
}
let store = new Vuex.Store({
actions,
})
let mocks = {
$t: jest.fn((t) => t),
}
const Wrapper = () => {
return shallowMount(KontoOverview, { localVue, store, mocks })
return shallowMount(KontoOverview, { localVue, mocks })
}
describe('shallow Mount', () => {
@ -44,37 +35,34 @@ describe('KontoOverview', () => {
expect(wrapper.find('gdd-table-stub').exists()).toBeTruthy()
})
it('calls "accountBalance" action from store on creation', () => {
const spy = jest.spyOn(actions, 'accountBalance')
expect(spy).toHaveBeenCalled()
})
it('updates transctions data when change-transactions is emitted', async () => {
wrapper.find('gdd-table-stub').vm.$emit('change-transactions', [0, 1])
await wrapper.vm.$nextTick()
expect(wrapper.vm.transactions).toEqual(expect.arrayContaining([0, 1]))
})
describe('setRows method', () => {
describe('updateBalance method', () => {
beforeEach(async () => {
wrapper.find('gdd-send-stub').vm.$emit('change-rows', {
row_form: false,
row_check: true,
row_thx: true,
wrapper.find('gdd-send-stub').vm.$emit('update-balance', {
ammount: 42,
})
await wrapper.vm.$nextTick()
})
it('updates row_form data when change-rows is emitted', () => {
expect(wrapper.vm.row_form).toBeFalsy()
it('emmits updateBalance with correct value', () => {
expect(wrapper.emitted('update-balance')).toEqual([[42]])
})
})
describe('toggleShowList method', () => {
beforeEach(async () => {
wrapper.setProps({ showTransactionList: false })
wrapper.find('gdd-send-stub').vm.$emit('toggle-show-list', true)
await wrapper.vm.$nextTick()
})
it('updates row_check data when change-rows is emitted', () => {
expect(wrapper.vm.row_check).toBeTruthy()
})
it('updates row_thx data when change-rows is emitted', () => {
expect(wrapper.vm.row_thx).toBeTruthy()
it('changes the value of property showTransactionList', () => {
expect(wrapper.vm.showTransactionList).toBeTruthy()
})
})
})

View File

@ -2,17 +2,21 @@
<div>
<base-header class="pb-6 pb-8 pt-5 pt-md-8 bg-transparent"></base-header>
<b-container fluid class="mt--7">
<gdd-status :row_form="row_form" />
<gdd-status
:balance="balance"
:gdt-balance="GdtBalance"
:show-transaction-list="showTransactionList"
/>
<br />
<gdd-send
:row_form="row_form"
:row_check="row_check"
:row_thx="row_thx"
@change-rows="setRows"
:balance="balance"
:show-transaction-list="showTransactionList"
@update-balance="updateBalance"
@toggle-show-list="toggleShowList"
/>
<hr />
<gdd-table
:row_form="row_form"
:show-transaction-list="showTransactionList"
:transactions="transactions"
@change-transactions="setTransactions"
/>
@ -28,25 +32,25 @@ export default {
name: 'Overview',
data() {
return {
row_form: true,
row_check: false,
row_thx: false,
transactions: [],
showTransactionList: true,
}
},
props: {
balance: { type: Number, default: 0 },
GdtBalance: { type: Number, default: 0 },
},
components: {
GddStatus,
GddSend,
GddTable,
},
created() {
this.$store.dispatch('accountBalance', this.$store.state.session_id)
},
methods: {
setRows(rows) {
this.row_form = rows.row_form
this.row_check = rows.row_check
this.row_thx = rows.row_thx
toggleShowList(bool) {
this.showTransactionList = bool
},
updateBalance(data) {
this.$emit('update-balance', data.ammount)
},
setTransactions(transactions) {
this.transactions = transactions

View File

@ -67,7 +67,7 @@
</b-alert>
<!-- <b-form-checkbox v-model="model.rememberMe">{{ $t('site.login.remember')}}</b-form-checkbox> -->
<div class="text-center">
<div class="text-center" ref="submitButton">
<base-button type="secondary" native-type="submit" class="my-4">
{{ $t('site.login.signin') }}
</base-button>
@ -82,7 +82,7 @@
{{ $t('site.login.forgot_pwd') }}
</router-link>
</b-col>
<b-col cols="6" class="text-right">
<b-col cols="6" class="text-right" v-show="allowRegister">
<router-link to="/register">
{{ $t('site.login.new_wallet') }}
</router-link>
@ -95,6 +95,7 @@
</template>
<script>
import loginAPI from '../../apis/loginAPI'
import CONFIG from '../../config'
export default {
name: 'login',
@ -106,10 +107,14 @@ export default {
// rememberMe: false
},
loginfail: false,
allowRegister: CONFIG.ALLOW_REGISTER,
}
},
methods: {
async onSubmit() {
let loader = this.$loading.show({
container: this.$refs.submitButton,
})
const result = await loginAPI.login(this.model.email, this.model.password)
if (result.success) {
this.$store.dispatch('login', {
@ -117,11 +122,14 @@ export default {
email: this.model.email,
})
this.$router.push('/overview')
loader.hide()
} else {
loader.hide()
this.loginfail = true
}
},
closeAlert() {
loader.hide()
this.loginfail = false
},
},

View File

@ -174,7 +174,7 @@ export default {
this.password,
)
if (result.success) {
this.$store.dispatch('createUser', {
this.$store.dispatch('login', {
session_id: result.result.data.session_id,
email: this.model.email,
})

View File

@ -1,17 +1,11 @@
const path = require('path')
const dotenv = require('dotenv-webpack')
function resolveSrc(_path) {
return path.join(__dirname, _path)
}
let vue_path = process.env.VUE_PATH
if (vue_path == undefined) {
vue_path = '/vue'
}
// vue.config.js
module.exports = {
devServer: {
port: process.env.PORT || 3000,
},
pluginOptions: {
i18n: {
locale: 'de',
@ -21,12 +15,12 @@ module.exports = {
},
},
lintOnSave: true,
publicPath: vue_path + '/',
publicPath: '/vue',
configureWebpack: {
// Set up all the aliases we use in our app.
resolve: {
alias: {
assets: resolveSrc('src/assets'),
assets: path.join(__dirname, 'src/assets'),
},
},
plugins: [new dotenv()],
@ -35,5 +29,5 @@ module.exports = {
// Enable CSS source maps.
sourceMap: process.env.NODE_ENV !== 'production',
},
outputDir: path.resolve(__dirname, './dist' + vue_path),
outputDir: path.resolve(__dirname, './dist'),
}

View File

@ -13348,6 +13348,11 @@ vue-loader@^15.7.0:
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
vue-loading-overlay@^3.4.2:
version "3.4.2"
resolved "https://registry.yarnpkg.com/vue-loading-overlay/-/vue-loading-overlay-3.4.2.tgz#34792a83218df1d35dff50121ce9fac2114f1c38"
integrity sha512-xcB+NPjl76eA0uggm707x3ZFgrNosZXpynHipyS3K+rrK1NztOV49R1LY+/4ij5W1KYANp7eRI2EIHrxCpmWAw==
vue-moment@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/vue-moment/-/vue-moment-4.1.0.tgz#092a8ff723a96c6f85a0a8e23ad30f0bf320f3b0"

View File

@ -6,3 +6,5 @@ src/cpsp/*.cpp
src/cpp/proto/
build*/
/skeema/gradido_login/insert/crypto_key.sql
src/LOCALE/messages.pot

View File

@ -10,13 +10,13 @@ namespace model
namespace table
{
PendingTask::PendingTask()
: mUserId(0), mHederaId(0), mTaskTypeId(TASK_TYPE_NONE)
: mUserId(0), mHederaId(0), mTaskTypeId(TASK_TYPE_NONE), mChildPendingTaskId(0), mParentPendingTaskId(0)
{
}
PendingTask::PendingTask(int userId, std::string serializedProtoRequest, TaskType type)
: mUserId(userId), mHederaId(0), mRequest((const unsigned char*)serializedProtoRequest.data(), serializedProtoRequest.size()),
mTaskTypeId(TASK_TYPE_NONE)
mTaskTypeId(TASK_TYPE_NONE), mChildPendingTaskId(0), mParentPendingTaskId(0)
{
}

View File

@ -1,5 +1,3 @@
server {
listen 80 ;
@ -8,12 +6,10 @@ server {
#include /etc/nginx/common/protect.conf;
#include /etc/nginx/common/protect_add_header.conf;
#include /etc/nginx/common/ssl.conf;
#include /etc/nginx/common/ssl.conf;
root /var/www/cakephp/webroot;
index index.php;
index index.php;
location ~ \.php$ {
fastcgi_pass community-server:9000;
@ -23,7 +19,6 @@ server {
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
include fastcgi_params;
}
location ~ /\.ht {
@ -31,65 +26,61 @@ server {
}
location /account {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
rewrite /account/(.*) /$1 break;
#proxy_next_upstream error timeout invalid_header http_502 non_idempotent;
proxy_pass http://login-server:1200;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
rewrite /account/(.*) /$1 break;
proxy_pass http://login-server:1200;
proxy_redirect off;
}
location /login_api {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
rewrite /login_api/(.*) /$1 break;
proxy_pass http://login-server:1201;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
rewrite /login_api/(.*) /$1 break;
proxy_pass http://login-server:1201;
proxy_redirect off;
}
location / {
try_files $uri $uri/ /index.php?$args;
try_files $uri $uri/ /index.php?$args;
}
location /vue {
location /vue/sockjs-node {
rewrite /vue/(.*) /$1;
}
location ~* \.(png) {
expires 1d;
rewrite /vue/(.*) /$1;
}
#try_files /vue/$uri /vue/$uri/ /index.html;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
#rewrite /vue/(.*) /$1 break;
proxy_pass http://frontend:3000;
proxy_redirect off;
}
proxy_pass http://frontend:8080;
location /sockjs-node {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_pass http://frontend:3000;
proxy_redirect off;
}
# access_log /var/log/nginx/access.log main;
}

View File

@ -25,4 +25,4 @@ COPY ./mariadb/.skeema.login ./gradido_login/.skeema
COPY ./community_server/db/skeema/ .
COPY ./mariadb/.skeema.community ./gradido_community/.skeema
CMD skeema push
CMD skeema push --allow-unsafe