mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into frontend_commit_hash
This commit is contained in:
commit
b196ef78b8
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -1,3 +1,5 @@
|
||||
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
||||
|
||||
<!--
|
||||
Please take a look at the issue templates at https://github.com/gradido/gradido/issues/new/choose
|
||||
before submitting a new issue. Following one of the issue templates will ensure maintainers can route your request efficiently.
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -4,6 +4,7 @@ about: Create a report to help us improve
|
||||
labels: bug
|
||||
title: 🐛 [Bug]
|
||||
---
|
||||
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
||||
|
||||
## 🐛 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.-->
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/devops_ticket.md
vendored
3
.github/ISSUE_TEMPLATE/devops_ticket.md
vendored
@ -1,9 +1,10 @@
|
||||
---
|
||||
name: 💥 DevOps ticket
|
||||
about: Help us manage our deployed App.
|
||||
about: Help us manage our deployed Software.
|
||||
labels: devops
|
||||
title: 💥 [DevOps]
|
||||
---
|
||||
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
||||
|
||||
## 💥 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.-->
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/epic.md
vendored
5
.github/ISSUE_TEMPLATE/epic.md
vendored
@ -4,9 +4,10 @@ about: Define a big development Step
|
||||
labels: epic
|
||||
title: 🌟 [EPIC]
|
||||
---
|
||||
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
||||
|
||||
<!-- THIS ISSUE-TYPE IS NOT FOR YOU! -->
|
||||
<!-- Proceed only if you know what you are doing - go chat with Team Gradido -->
|
||||
<!-- Visit the Gradido Discord: https://discord.gg/kA3zBAKQDC -->
|
||||
<!-- Proceed only if you know what you are doing - have a chat with Project's Team first -->
|
||||
|
||||
## 🌟 EPIC
|
||||
<!-- Describe your Epic in detail. Include screenshots and drawings -->
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -4,6 +4,7 @@ about: Suggest an idea for this project
|
||||
labels: feature
|
||||
title: 🚀 [Feature]
|
||||
---
|
||||
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
||||
|
||||
## 🚀 Feature
|
||||
<!-- Give a short summary of the Feature. Use Screenshots if you want. -->
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/question.md
vendored
8
.github/ISSUE_TEMPLATE/question.md
vendored
@ -1,11 +1,13 @@
|
||||
---
|
||||
name: 💬 Question
|
||||
about: If you need help understanding Gradido.
|
||||
about: If you need help understanding our Software.
|
||||
labels: question
|
||||
title: 💬 [Question]
|
||||
---
|
||||
<!-- Chat with Team Gradido -->
|
||||
<!-- If you need an answer right away, visit the Gradido Discord: https://discord.gg/kA3zBAKQDC -->
|
||||
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
||||
|
||||
<!-- Question the project's team -->
|
||||
<!-- If you need an answer right away, consider to take other means of communication with the project's team -->
|
||||
|
||||
## 💬 Question
|
||||
<!-- Describe your Question in detail. Include screenshots and drawings if needed. -->
|
||||
|
||||
@ -4,6 +4,7 @@ about: Help us improve our code by refactoring it.
|
||||
labels: refactor
|
||||
title: 🔧 [Refactor]
|
||||
---
|
||||
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
||||
|
||||
## 🔧 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.-->
|
||||
5
.github/ISSUE_TEMPLATE/release.md
vendored
5
.github/ISSUE_TEMPLATE/release.md
vendored
@ -4,9 +4,10 @@ about: Define a Release
|
||||
labels: release
|
||||
title: 🏅 [RELEASE]
|
||||
---
|
||||
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
||||
|
||||
<!-- THIS ISSUE-TYPE IS NOT FOR YOU! -->
|
||||
<!-- Proceed only if you know what you are doing - go chat with Team Gradido -->
|
||||
<!-- Visit the Gradido Discord: https://discord.gg/kA3zBAKQDC -->
|
||||
<!-- Proceed only if you know what you are doing - have a chat with Project's Team first -->
|
||||
|
||||
## 🏅 RELEASE
|
||||
<!-- Describe your Release in detail. Include screenshots and drawings -->
|
||||
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,3 +1,5 @@
|
||||
<!-- You can find the latest issue templates here https://github.com/ulfgebhardt/issue-templates -->
|
||||
|
||||
## 🍰 Pullrequest
|
||||
<!-- Describe the Pullrequest. Use Screenshots if possible. -->
|
||||
|
||||
|
||||
35
.github/workflows/test.yml
vendored
35
.github/workflows/test.yml
vendored
@ -170,7 +170,7 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# BUILD NGINX DOCKER IMAGE #############################################
|
||||
# BUILD NGINX DOCKER IMAGE ###############################################
|
||||
##########################################################################
|
||||
- name: nginx | Build `test` image
|
||||
run: |
|
||||
@ -182,6 +182,35 @@ jobs:
|
||||
name: docker-nginx-test
|
||||
path: /tmp/nginx.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: LOCALES FRONTEND ######################################################
|
||||
##############################################################################
|
||||
locales_frontend:
|
||||
name: Locales - Frontend
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_frontend]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Frontend)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-frontend-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/frontend.tar
|
||||
##########################################################################
|
||||
# LOCALES FRONTEND #######################################################
|
||||
##########################################################################
|
||||
- name: frontend | Locales
|
||||
run: docker run --rm gradido/frontend:test yarn run locales
|
||||
|
||||
##############################################################################
|
||||
# JOB: LINT FRONTEND #########################################################
|
||||
##############################################################################
|
||||
@ -206,7 +235,7 @@ jobs:
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/frontend.tar
|
||||
##########################################################################
|
||||
# LINT FRONTEND ###########################################################
|
||||
# LINT FRONTEND ##########################################################
|
||||
##########################################################################
|
||||
- name: frontend | Lint
|
||||
run: docker run --rm gradido/frontend:test yarn run lint
|
||||
@ -316,7 +345,7 @@ jobs:
|
||||
report_name: Coverage Frontend
|
||||
type: lcov
|
||||
result_path: ./coverage/lcov.info
|
||||
min_coverage: 60
|
||||
min_coverage: 67
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
|
||||
@ -39,4 +39,3 @@ git submodule update --recursive --init
|
||||
## Useful Links
|
||||
|
||||
- [Gradido.net](https://gradido.net/)
|
||||
- [Discord](https://discord.gg/kA3zBAKQDC)
|
||||
|
||||
@ -9,4 +9,9 @@ DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=
|
||||
DB_DATABASE=gradido_community
|
||||
DB_DATABASE=gradido_community
|
||||
#KLICKTIPP_USER=
|
||||
#KLICKTIPP_PASSWORD=
|
||||
#KLICKTIPP_APIKEY_DE=
|
||||
#KLICKTIPP_APIKEY_EN=
|
||||
#KLICKTIPP=true
|
||||
@ -4,8 +4,35 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [1.3.1](https://github.com/gradido/gradido/compare/1.3.1...1.3.1)
|
||||
#### [1.4.0](https://github.com/gradido/gradido/compare/1.3.1...1.4.0)
|
||||
|
||||
- Integration of the KlicktippAPI to the User management [`#753`](https://github.com/gradido/gradido/pull/753)
|
||||
- feat: Locale Management [`#809`](https://github.com/gradido/gradido/pull/809)
|
||||
- feat: Increase Coverage Test Frontend [`#812`](https://github.com/gradido/gradido/pull/812)
|
||||
- My thoughts to jwt [`#800`](https://github.com/gradido/gradido/pull/800)
|
||||
- Remove discord link [`#808`](https://github.com/gradido/gradido/pull/808)
|
||||
- refactor: Pagination Buttons [`#806`](https://github.com/gradido/gradido/pull/806)
|
||||
- add new components selectLanguage and Usercard_Language [`#798`](https://github.com/gradido/gradido/pull/798)
|
||||
- gdt transaction with arrays and without slots [`#793`](https://github.com/gradido/gradido/pull/793)
|
||||
- feat: New JWT in Every Authenticated Response [`#797`](https://github.com/gradido/gradido/pull/797)
|
||||
- fix euro after comma gdt view [`#799`](https://github.com/gradido/gradido/pull/799)
|
||||
- different path for checkEmail and resetPassword [`#796`](https://github.com/gradido/gradido/pull/796)
|
||||
- fix old frontend wrong display of event gdt [`#795`](https://github.com/gradido/gradido/pull/795)
|
||||
- update scripts and doc for login-server setup without docker [`#783`](https://github.com/gradido/gradido/pull/783)
|
||||
- fix: Flaky Test for Logout [`#792`](https://github.com/gradido/gradido/pull/792)
|
||||
- Analyse Iota Colored Coins [`#779`](https://github.com/gradido/gradido/pull/779)
|
||||
- Fix Bug in displaying GDT in Old frontend [`#788`](https://github.com/gradido/gradido/pull/788)
|
||||
- feat: JSON Web Token for Authentification [`#777`](https://github.com/gradido/gradido/pull/777)
|
||||
- select language during registration [`#778`](https://github.com/gradido/gradido/pull/778)
|
||||
- Fix missing gdt id [`#782`](https://github.com/gradido/gradido/pull/782)
|
||||
- Remove Migrations from community server [`#776`](https://github.com/gradido/gradido/pull/776)
|
||||
- fix_database_migrations [`#775`](https://github.com/gradido/gradido/pull/775)
|
||||
- decay with the value 0 is no longer displayed [`#773`](https://github.com/gradido/gradido/pull/773)
|
||||
- database_migrations [`#770`](https://github.com/gradido/gradido/pull/770)
|
||||
- backend_version [`#756`](https://github.com/gradido/gradido/pull/756)
|
||||
- issue_type_release [`#769`](https://github.com/gradido/gradido/pull/769)
|
||||
- some_docu [`#771`](https://github.com/gradido/gradido/pull/771)
|
||||
- database_reachable_in_testmode [`#768`](https://github.com/gradido/gradido/pull/768)
|
||||
- logo changed, old logos and icons deleted [`#734`](https://github.com/gradido/gradido/pull/734)
|
||||
- change default value of communty url [`#755`](https://github.com/gradido/gradido/pull/755)
|
||||
- feat: Testing Tabs of TransactionLists [`#737`](https://github.com/gradido/gradido/pull/737)
|
||||
@ -14,9 +41,9 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- feat: Vue Apollo Client [`#701`](https://github.com/gradido/gradido/pull/701)
|
||||
- change text from Geld to Gradidos [`#711`](https://github.com/gradido/gradido/pull/711)
|
||||
- fix fix [`#728`](https://github.com/gradido/gradido/pull/728)
|
||||
- generate updated pot file and update po file [`1569845`](https://github.com/gradido/gradido/commit/15698459f8be9d5111757393f5f3b4558e60c7b1)
|
||||
- Implementation of the FormUserMail Tests. And review changes. [`421aba2`](https://github.com/gradido/gradido/commit/421aba22ff1620c534d6ab8b6592b5129e275265)
|
||||
- test for GDT transaction list [`bfe8069`](https://github.com/gradido/gradido/commit/bfe806988e309b88d3f8f3f3aa0cd9ca86319300)
|
||||
- sort locales [`ec12a28`](https://github.com/gradido/gradido/commit/ec12a28f81577d530f58b42b7f8c2c7d20dffd64)
|
||||
- feat: Unify and Sort Locales [`aba4f4d`](https://github.com/gradido/gradido/commit/aba4f4d20e0a13016e3528a1c5c30c111eb3a9f1)
|
||||
- feat: Increase Coverage [`3c061bc`](https://github.com/gradido/gradido/commit/3c061bcb8d1a3a47442ed6a351e1428e15b314aa)
|
||||
|
||||
#### [1.3.1](https://github.com/gradido/gradido/compare/1.3.0...1.3.1)
|
||||
|
||||
|
||||
4563
backend/package-lock.json
generated
Normal file
4563
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido-backend",
|
||||
"version": "1.3.1",
|
||||
"version": "1.4.0",
|
||||
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/backend",
|
||||
|
||||
77
backend/src/apis/KlicktippController.ts
Normal file
77
backend/src/apis/KlicktippController.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { KlicktippConnector } from './klicktippConnector'
|
||||
import CONFIG from '../config'
|
||||
|
||||
const klicktippConnector = new KlicktippConnector()
|
||||
|
||||
export const signIn = async (
|
||||
email: string,
|
||||
language: string,
|
||||
firstName?: string,
|
||||
lastName?: string,
|
||||
): Promise<boolean> => {
|
||||
const fields = {
|
||||
fieldFirstName: firstName,
|
||||
fieldLastName: lastName,
|
||||
}
|
||||
const apiKey = language === 'de' ? CONFIG.KLICKTIPP_APIKEY_DE : CONFIG.KLICKTIPP_APIKEY_EN
|
||||
const result = await klicktippConnector.signin(apiKey, email, fields)
|
||||
return result
|
||||
}
|
||||
|
||||
export const signout = async (email: string, language: string): Promise<boolean> => {
|
||||
const apiKey = language === 'de' ? CONFIG.KLICKTIPP_APIKEY_DE : CONFIG.KLICKTIPP_APIKEY_EN
|
||||
const result = await klicktippConnector.signoff(apiKey, email)
|
||||
return result
|
||||
}
|
||||
|
||||
export const unsubscribe = async (email: string): Promise<boolean> => {
|
||||
const isLogin = await loginKlicktippUser()
|
||||
if (isLogin) {
|
||||
return await klicktippConnector.unsubscribe(email)
|
||||
}
|
||||
throw new Error(`Could not unsubscribe ${email}`)
|
||||
}
|
||||
|
||||
export const getKlickTippUser = async (email: string): Promise<any> => {
|
||||
const isLogin = await loginKlicktippUser()
|
||||
if (isLogin) {
|
||||
const subscriberId = await klicktippConnector.subscriberSearch(email)
|
||||
const result = await klicktippConnector.subscriberGet(subscriberId)
|
||||
return result
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const loginKlicktippUser = async (): Promise<boolean> => {
|
||||
return await klicktippConnector.login(CONFIG.KLICKTIPP_USER, CONFIG.KLICKTIPP_PASSWORD)
|
||||
}
|
||||
|
||||
export const logoutKlicktippUser = async (): Promise<boolean> => {
|
||||
return await klicktippConnector.logout()
|
||||
}
|
||||
|
||||
export const untagUser = async (email: string, tagId: string): Promise<boolean> => {
|
||||
const isLogin = await loginKlicktippUser()
|
||||
if (isLogin) {
|
||||
return await klicktippConnector.untag(email, tagId)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const tagUser = async (email: string, tagIds: string): Promise<boolean> => {
|
||||
const isLogin = await loginKlicktippUser()
|
||||
if (isLogin) {
|
||||
return await klicktippConnector.tag(email, tagIds)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const getKlicktippTagMap = async () => {
|
||||
const isLogin = await loginKlicktippUser()
|
||||
if (isLogin) {
|
||||
return await klicktippConnector.tagIndex()
|
||||
}
|
||||
return ''
|
||||
}
|
||||
620
backend/src/apis/klicktippConnector.ts
Normal file
620
backend/src/apis/klicktippConnector.ts
Normal file
@ -0,0 +1,620 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import axios, { AxiosRequestConfig, Method } from 'axios'
|
||||
|
||||
export class KlicktippConnector {
|
||||
private baseURL: string
|
||||
private sessionName: string
|
||||
private sessionId: string
|
||||
private error: string
|
||||
|
||||
constructor(service?: string) {
|
||||
this.baseURL = service !== undefined ? service : 'https://api.klicktipp.com'
|
||||
this.sessionName = ''
|
||||
this.sessionId = ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last error
|
||||
*
|
||||
* @return string an error description of the last error
|
||||
*/
|
||||
getLastError(): string {
|
||||
const result = this.error
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* login
|
||||
*
|
||||
* @param username The login name of the user to login.
|
||||
* @param password The password of the user.
|
||||
* @return TRUE on success
|
||||
*/
|
||||
async login(username: string, password: string): Promise<boolean> {
|
||||
if (!(username.length > 0 && password.length > 0)) {
|
||||
throw new Error('Klicktipp Login failed: Illegal Arguments')
|
||||
}
|
||||
|
||||
const res = await this.httpRequest('/account/login', 'POST', { username, password }, false)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
this.sessionId = res.data.sessid
|
||||
this.sessionName = res.data.session_name
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
throw new Error(`Klicktipp Login failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs out the user currently logged in.
|
||||
*
|
||||
* @return TRUE on success
|
||||
*/
|
||||
async logout(): Promise<boolean> {
|
||||
const res = await this.httpRequest('/account/logout', 'POST')
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
this.sessionId = ''
|
||||
this.sessionName = ''
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
throw new Error(`Klicktipp Logout failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all subscription processes (lists) of the logged in user. Requires to be logged in.
|
||||
*
|
||||
* @return A associative obeject <list id> => <list name>
|
||||
*/
|
||||
async subscriptionProcessIndex(): Promise<any> {
|
||||
const res = await this.httpRequest('/list', 'GET', {}, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
|
||||
throw new Error(`Klicktipp Subscription process index failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscription process (list) definition. Requires to be logged in.
|
||||
*
|
||||
* @param listid The id of the subscription process
|
||||
*
|
||||
* @return An object representing the Klicktipp subscription process.
|
||||
*/
|
||||
async subscriptionProcessGet(listid: string): Promise<any> {
|
||||
if (!listid || listid === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// retrieve
|
||||
const res = await this.httpRequest(`/subscriber/${listid}`, 'GET', {}, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
|
||||
throw new Error(`Klicktipp Subscription process get failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscription process (list) redirection url for given subscription.
|
||||
*
|
||||
* @param listid The id of the subscription process.
|
||||
* @param email The email address of the subscriber.
|
||||
*
|
||||
* @return A redirection url as defined in the subscription process.
|
||||
*/
|
||||
async subscriptionProcessRedirect(listid: string, email: string): Promise<any> {
|
||||
if (!listid || listid === '' || !email || email === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// update
|
||||
const data = { listid, email }
|
||||
const res = await this.httpRequest('/list/redirect', 'POST', data)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Klicktipp Subscription process get redirection url failed: ${res.response.statusText}`,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all manual tags of the logged in user. Requires to be logged in.
|
||||
*
|
||||
* @return A associative object <tag id> => <tag name>
|
||||
*/
|
||||
async tagIndex(): Promise<any> {
|
||||
const res = await this.httpRequest('/tag', 'GET', {}, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
|
||||
throw new Error(`Klicktipp Tag index failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a tag definition. Requires to be logged in.
|
||||
*
|
||||
* @param tagid The tag id.
|
||||
*
|
||||
* @return An object representing the Klicktipp tag object.
|
||||
*/
|
||||
async tagGet(tagid: string): Promise<any> {
|
||||
if (!tagid || tagid === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
const res = await this.httpRequest(`/tag/${tagid}`, 'GET', {}, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
|
||||
throw new Error(`Klicktipp Tag get failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new manual tag. Requires to be logged in.
|
||||
*
|
||||
* @param name The name of the tag.
|
||||
* @param text (optional) An additional description of the tag.
|
||||
*
|
||||
* @return The id of the newly created tag or false if failed.
|
||||
*/
|
||||
async tagCreate(name: string, text?: string): Promise<boolean> {
|
||||
if (!name || name === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
const data = {
|
||||
name,
|
||||
text: text !== undefined ? text : '',
|
||||
}
|
||||
const res = await this.httpRequest('/tag', 'POST', data, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
|
||||
throw new Error(`Klicktipp Tag creation failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a tag. Requires to be logged in.
|
||||
*
|
||||
* @param tagid The tag id used to identify which tag to modify.
|
||||
* @param name (optional) The new tag name. Set empty to leave it unchanged.
|
||||
* @param text (optional) The new tag description. Set empty to leave it unchanged.
|
||||
*
|
||||
* @return TRUE on success
|
||||
*/
|
||||
async tagUpdate(tagid: string, name?: string, text?: string): Promise<boolean> {
|
||||
if (!tagid || tagid === '' || (name === '' && text === '')) {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
const data = {
|
||||
name: name !== undefined ? name : '',
|
||||
text: text !== undefined ? text : '',
|
||||
}
|
||||
|
||||
const res = await this.httpRequest(`/tag/${tagid}`, 'PUT', data, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return true
|
||||
}
|
||||
|
||||
throw new Error(`Klicktipp Tag update failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a tag. Requires to be logged in.
|
||||
*
|
||||
* @param tagid The user id of the user to delete.
|
||||
*
|
||||
* @return TRUE on success
|
||||
*/
|
||||
async tagDelete(tagid: string): Promise<boolean> {
|
||||
if (!tagid || tagid === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
const res = await this.httpRequest(`/tag/${tagid}`, 'DELETE')
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return true
|
||||
}
|
||||
|
||||
throw new Error(`Klicktipp Tag deletion failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all contact fields of the logged in user. Requires to be logged in.
|
||||
*
|
||||
* @return A associative object <field id> => <field name>
|
||||
*/
|
||||
async fieldIndex(): Promise<any> {
|
||||
const res = await this.httpRequest('/field', 'GET', {}, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
|
||||
throw new Error(`Klicktipp Field index failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe an email. Requires to be logged in.
|
||||
*
|
||||
* @param email The email address of the subscriber.
|
||||
* @param listid (optional) The id subscription process.
|
||||
* @param tagid (optional) The id of the manual tag the subscriber will be tagged with.
|
||||
* @param fields (optional) Additional fields of the subscriber.
|
||||
*
|
||||
* @return An object representing the Klicktipp subscriber object.
|
||||
*/
|
||||
async subscribe(
|
||||
email: string,
|
||||
listid?: number,
|
||||
tagid?: number,
|
||||
fields?: any,
|
||||
smsnumber?: string,
|
||||
): Promise<any> {
|
||||
if ((!email || email === '') && smsnumber === '') {
|
||||
throw new Error('Illegal Arguments')
|
||||
}
|
||||
// subscribe
|
||||
const data = {
|
||||
email,
|
||||
fields: fields !== undefined ? fields : {},
|
||||
smsnumber: smsnumber !== undefined ? smsnumber : '',
|
||||
listid: listid !== undefined ? listid : 0,
|
||||
tagid: tagid !== undefined ? tagid : 0,
|
||||
}
|
||||
|
||||
const res = await this.httpRequest('/subscriber', 'POST', data, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
throw new Error(`Klicktipp Subscription failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe an email. Requires to be logged in.
|
||||
*
|
||||
* @param email The email address of the subscriber.
|
||||
*
|
||||
* @return TRUE on success
|
||||
*/
|
||||
async unsubscribe(email: string): Promise<boolean> {
|
||||
if (!email || email === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// unsubscribe
|
||||
const data = { email }
|
||||
|
||||
const res = await this.httpRequest('/subscriber/unsubscribe', 'POST', data, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return true
|
||||
}
|
||||
throw new Error(`Klicktipp Unsubscription failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag an email. Requires to be logged in.
|
||||
*
|
||||
* @param email The email address of the subscriber.
|
||||
* @param tagids an array of the manual tag(s) the subscriber will be tagged with.
|
||||
*
|
||||
* @return TRUE on success
|
||||
*/
|
||||
async tag(email: string, tagids: string): Promise<boolean> {
|
||||
if (!email || email === '' || !tagids || tagids === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// tag
|
||||
const data = {
|
||||
email,
|
||||
tagids,
|
||||
}
|
||||
|
||||
const res = await this.httpRequest('/subscriber/tag', 'POST', data, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
throw new Error(`Klicktipp Tagging failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Untag an email. Requires to be logged in.
|
||||
*
|
||||
* @param mixed $email The email address of the subscriber.
|
||||
* @param mixed $tagid The id of the manual tag that will be removed from the subscriber.
|
||||
*
|
||||
* @return TRUE on success.
|
||||
*/
|
||||
async untag(email: string, tagid: string): Promise<boolean> {
|
||||
if (!email || email === '' || !tagid || tagid === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// subscribe
|
||||
const data = {
|
||||
email,
|
||||
tagid,
|
||||
}
|
||||
|
||||
const res = await this.httpRequest('/subscriber/untag', 'POST', data, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return true
|
||||
}
|
||||
throw new Error(`Klicktipp Untagging failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resend an autoresponder for an email address. Requires to be logged in.
|
||||
*
|
||||
* @param email A valid email address
|
||||
* @param autoresponder An id of the autoresponder
|
||||
*
|
||||
* @return TRUE on success
|
||||
*/
|
||||
async resend(email: string, autoresponder: string): Promise<boolean> {
|
||||
if (!email || email === '' || !autoresponder || autoresponder === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// resend/reset autoresponder
|
||||
const data = { email, autoresponder }
|
||||
|
||||
const res = await this.httpRequest('/subscriber/resend', 'POST', data, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return true
|
||||
}
|
||||
throw new Error(`Klicktipp Resend failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active subscribers. Requires to be logged in.
|
||||
*
|
||||
* @return An array of subscriber ids.
|
||||
*/
|
||||
async subscriberIndex(): Promise<[string]> {
|
||||
const res = await this.httpRequest('/subscriber', 'GET', undefined, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
throw new Error(`Klicktipp Subscriber index failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscriber information. Requires to be logged in.
|
||||
*
|
||||
* @param subscriberid The subscriber id.
|
||||
*
|
||||
* @return An object representing the Klicktipp subscriber.
|
||||
*/
|
||||
async subscriberGet(subscriberid: string): Promise<any> {
|
||||
if (!subscriberid || subscriberid === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// retrieve
|
||||
const res = await this.httpRequest(`/subscriber/${subscriberid}`, 'GET', {}, true)
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
throw new Error(`Klicktipp Subscriber get failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subscriber id by email. Requires to be logged in.
|
||||
*
|
||||
* @param email The email address of the subscriber.
|
||||
*
|
||||
* @return The id of the subscriber. Use subscriber_get to get subscriber details.
|
||||
*/
|
||||
async subscriberSearch(email: string): Promise<any> {
|
||||
if (!email || email === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
// search
|
||||
const data = { email }
|
||||
const res = await this.httpRequest('/subscriber/search', 'POST', data, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
throw new Error(`Klicktipp Subscriber search failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active subscribers tagged with the given tag id. Requires to be logged in.
|
||||
*
|
||||
* @param tagid The id of the tag.
|
||||
*
|
||||
* @return An array with id -> subscription date of the tagged subscribers. Use subscriber_get to get subscriber details.
|
||||
*/
|
||||
async subscriberTagged(tagid: string): Promise<any> {
|
||||
if (!tagid || tagid === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// search
|
||||
const data = { tagid }
|
||||
const res = await this.httpRequest('/subscriber/tagged', 'POST', data, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return res.data
|
||||
}
|
||||
throw new Error(`Klicktipp subscriber tagged failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a subscriber. Requires to be logged in.
|
||||
*
|
||||
* @param subscriberid The id of the subscriber to update.
|
||||
* @param fields (optional) The fields of the subscriber to update
|
||||
* @param newemail (optional) The new email of the subscriber to update
|
||||
*
|
||||
* @return TRUE on success
|
||||
*/
|
||||
async subscriberUpdate(
|
||||
subscriberid: string,
|
||||
fields?: any,
|
||||
newemail?: string,
|
||||
newsmsnumber?: string,
|
||||
): Promise<boolean> {
|
||||
if (!subscriberid || subscriberid === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// update
|
||||
const data = {
|
||||
fields: fields !== undefined ? fields : {},
|
||||
newemail: newemail !== undefined ? newemail : '',
|
||||
newsmsnumber: newsmsnumber !== undefined ? newsmsnumber : '',
|
||||
}
|
||||
const res = await this.httpRequest(`/subscriber/${subscriberid}`, 'PUT', data, true)
|
||||
if (!res.isAxiosError) {
|
||||
return true
|
||||
}
|
||||
throw new Error(`Klicktipp Subscriber update failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a subscribe. Requires to be logged in.
|
||||
*
|
||||
* @param subscriberid The id of the subscriber to update.
|
||||
*
|
||||
* @return TRUE on success.
|
||||
*/
|
||||
async subscriberDelete(subscriberid: string): Promise<boolean> {
|
||||
if (!subscriberid || subscriberid === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// delete
|
||||
const res = await this.httpRequest(`/subscriber/${subscriberid}`, 'DELETE', {}, true)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return true
|
||||
}
|
||||
throw new Error(`Klicktipp Subscriber deletion failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe an email. Requires an api key.
|
||||
*
|
||||
* @param apikey The api key (listbuildng configuration).
|
||||
* @param email The email address of the subscriber.
|
||||
* @param fields (optional) Additional fields of the subscriber.
|
||||
*
|
||||
* @return A redirection url as defined in the subscription process.
|
||||
*/
|
||||
async signin(apikey: string, email: string, fields?: any, smsnumber?: string): Promise<boolean> {
|
||||
if (!apikey || apikey === '' || ((!email || email === '') && smsnumber === '')) {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// subscribe
|
||||
const data = {
|
||||
apikey,
|
||||
email,
|
||||
fields: fields !== undefined ? fields : {},
|
||||
smsnumber: smsnumber !== undefined ? smsnumber : '',
|
||||
}
|
||||
|
||||
const res = await this.httpRequest('/subscriber/signin', 'POST', data)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return true
|
||||
}
|
||||
throw new Error(`Klicktipp Subscription failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Untag an email. Requires an api key.
|
||||
*
|
||||
* @param apikey The api key (listbuildng configuration).
|
||||
* @param email The email address of the subscriber.
|
||||
*
|
||||
* @return TRUE on success
|
||||
*/
|
||||
async signout(apikey: string, email: string): Promise<boolean> {
|
||||
if (!apikey || apikey === '' || !email || email === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// untag
|
||||
const data = { apikey, email }
|
||||
const res = await this.httpRequest('/subscriber/signout', 'POST', data)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return true
|
||||
}
|
||||
throw new Error(`Klicktipp Untagging failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe an email. Requires an api key.
|
||||
*
|
||||
* @param apikey The api key (listbuildng configuration).
|
||||
* @param email The email address of the subscriber.
|
||||
*
|
||||
* @return TRUE on success
|
||||
*/
|
||||
async signoff(apikey: string, email: string): Promise<boolean> {
|
||||
if (!apikey || apikey === '' || !email || email === '') {
|
||||
throw new Error('Klicktipp Illegal Arguments')
|
||||
}
|
||||
|
||||
// unsubscribe
|
||||
const data = { apikey, email }
|
||||
const res = await this.httpRequest('/subscriber/signoff', 'POST', data)
|
||||
|
||||
if (!res.isAxiosError) {
|
||||
return true
|
||||
}
|
||||
throw new Error(`Klicktipp Unsubscription failed: ${res.response.statusText}`)
|
||||
}
|
||||
|
||||
async httpRequest(path: string, method?: Method, data?: any, usesession?: boolean): Promise<any> {
|
||||
if (method === undefined) {
|
||||
method = 'GET'
|
||||
}
|
||||
const options: AxiosRequestConfig = {
|
||||
baseURL: this.baseURL,
|
||||
method,
|
||||
url: path,
|
||||
data,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Content: 'application/json',
|
||||
Cookie:
|
||||
usesession && this.sessionName !== '' ? `${this.sessionName}=${this.sessionId}` : '',
|
||||
},
|
||||
}
|
||||
|
||||
return axios(options)
|
||||
.then((res) => res)
|
||||
.catch((error) => error)
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import { AuthChecker } from 'type-graphql'
|
||||
import decode from '../jwt/decode'
|
||||
import { apiGet } from '../apis/loginAPI'
|
||||
import { apiGet } from '../apis/HttpRequest'
|
||||
import CONFIG from '../config'
|
||||
import encode from '../jwt/encode'
|
||||
|
||||
|
||||
@ -21,9 +21,18 @@ const database = {
|
||||
DB_DATABASE: process.env.DB_DATABASE || 'gradido_community',
|
||||
}
|
||||
|
||||
const klicktipp = {
|
||||
KLICKTIPP: process.env.KLICKTIPP === 'true' || false,
|
||||
KLICKTTIPP_API_URL: process.env.KLICKTIPP_API_URL || 'https://api.klicktipp.com',
|
||||
KLICKTIPP_USER: process.env.KLICKTIPP_USER || 'gradido_test',
|
||||
KLICKTIPP_PASSWORD: process.env.KLICKTIPP_PASSWORD || 'secret321',
|
||||
KLICKTIPP_APIKEY_DE: process.env.KLICKTIPP_APIKEY_DE || 'SomeFakeKeyDE',
|
||||
KLICKTIPP_APIKEY_EN: process.env.KLICKTIPP_APIKEY_EN || 'SomeFakeKeyEN',
|
||||
}
|
||||
|
||||
// This is needed by graphql-directive-auth
|
||||
process.env.APP_SECRET = server.JWT_SECRET
|
||||
|
||||
const CONFIG = { ...server, ...database }
|
||||
const CONFIG = { ...server, ...database, ...klicktipp }
|
||||
|
||||
export default CONFIG
|
||||
|
||||
10
backend/src/graphql/inputs/KlickTippInputs.ts
Normal file
10
backend/src/graphql/inputs/KlickTippInputs.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class SubscribeNewsletterArguments {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
language: string
|
||||
}
|
||||
@ -22,6 +22,9 @@ export class CreateUserArgs {
|
||||
|
||||
@Field(() => String)
|
||||
password: string
|
||||
|
||||
@Field(() => String)
|
||||
language: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
@ -56,6 +59,9 @@ export class UpdateUserInfosArgs {
|
||||
@Field({ nullable: true })
|
||||
language?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
publisherId?: number
|
||||
|
||||
@Field({ nullable: true })
|
||||
password?: string
|
||||
|
||||
|
||||
29
backend/src/graphql/models/CheckEmailResponse.ts
Normal file
29
backend/src/graphql/models/CheckEmailResponse.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class CheckEmailResponse {
|
||||
constructor(json: any) {
|
||||
this.sessionId = json.session_id
|
||||
this.email = json.user.email
|
||||
this.language = json.user.language
|
||||
this.firstName = json.user.first_name
|
||||
this.lastName = json.user.last_name
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
sessionId: number
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
firstName: string
|
||||
|
||||
@Field(() => String)
|
||||
lastName: string
|
||||
|
||||
@Field(() => String)
|
||||
language: string
|
||||
}
|
||||
13
backend/src/graphql/models/KlickTipp.ts
Normal file
13
backend/src/graphql/models/KlickTipp.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class KlickTipp {
|
||||
constructor(json: any) {
|
||||
this.newsletterState = json.status === 'Subscribed'
|
||||
}
|
||||
|
||||
@Field(() => Boolean)
|
||||
newsletterState: boolean
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
import { KlickTipp } from './KlickTipp'
|
||||
|
||||
@ObjectType()
|
||||
export class User {
|
||||
@ -16,6 +17,7 @@ export class User {
|
||||
this.username = json.username
|
||||
this.description = json.description
|
||||
this.language = json.language
|
||||
this.publisherId = json.publisher_id
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
@ -59,9 +61,11 @@ export class User {
|
||||
/* I suggest to have a group as type here
|
||||
@Field(() => ID)
|
||||
groupId: number
|
||||
|
||||
// what is puvlisherId?
|
||||
@Field(() => ID)
|
||||
*/
|
||||
// what is publisherId?
|
||||
@Field(() => Number)
|
||||
publisherId: number
|
||||
*/
|
||||
|
||||
@Field(() => KlickTipp)
|
||||
klickTipp: KlickTipp
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
import { Resolver, Query, Ctx, Authorized } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { Balance } from '../models/Balance'
|
||||
import { apiGet } from '../../apis/loginAPI'
|
||||
import { apiGet } from '../../apis/HttpRequest'
|
||||
|
||||
@Resolver()
|
||||
export class BalanceResolver {
|
||||
|
||||
@ -5,7 +5,7 @@ import { Resolver, Query, Args, Ctx, Authorized } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { GdtEntryList } from '../models/GdtEntryList'
|
||||
import { GdtTransactionSessionIdInput } from '../inputs/GdtInputs'
|
||||
import { apiGet } from '../../apis/loginAPI'
|
||||
import { apiGet } from '../../apis/HttpRequest'
|
||||
|
||||
@Resolver()
|
||||
export class GdtResolver {
|
||||
|
||||
40
backend/src/graphql/resolvers/KlicktippResolver.ts
Normal file
40
backend/src/graphql/resolvers/KlicktippResolver.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
import { Resolver, Query, Authorized, Arg, Mutation, Args } from 'type-graphql'
|
||||
import {
|
||||
getKlickTippUser,
|
||||
getKlicktippTagMap,
|
||||
unsubscribe,
|
||||
signIn,
|
||||
} from '../../apis/KlicktippController'
|
||||
import { SubscribeNewsletterArguments } from '../inputs/KlickTippInputs'
|
||||
|
||||
@Resolver()
|
||||
export class KlicktippResolver {
|
||||
@Authorized()
|
||||
@Query(() => String)
|
||||
async getKlicktippUser(@Arg('email') email: string): Promise<string> {
|
||||
return await getKlickTippUser(email)
|
||||
}
|
||||
|
||||
@Authorized()
|
||||
@Query(() => String)
|
||||
async getKlicktippTagMap(): Promise<string> {
|
||||
return await getKlicktippTagMap()
|
||||
}
|
||||
|
||||
@Authorized()
|
||||
@Mutation(() => Boolean)
|
||||
async unsubscribeNewsletter(@Arg('email') email: string): Promise<boolean> {
|
||||
return await unsubscribe(email)
|
||||
}
|
||||
|
||||
@Authorized()
|
||||
@Mutation(() => Boolean)
|
||||
async subscribeNewsletter(
|
||||
@Args() { email, language }: SubscribeNewsletterArguments,
|
||||
): Promise<boolean> {
|
||||
return await signIn(email, language)
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ import { Resolver, Query, Args, Authorized, Ctx } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { TransactionList } from '../models/Transaction'
|
||||
import { TransactionListInput, TransactionSendArgs } from '../inputs/TransactionInput'
|
||||
import { apiGet, apiPost } from '../../apis/loginAPI'
|
||||
import { apiGet, apiPost } from '../../apis/HttpRequest'
|
||||
|
||||
@Resolver()
|
||||
export class TransactionResolver {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
import { Resolver, Query, Args, Arg, Authorized, Ctx } from 'type-graphql'
|
||||
import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { CheckUsernameResponse } from '../models/CheckUsernameResponse'
|
||||
import { LoginViaVerificationCode } from '../models/LoginViaVerificationCode'
|
||||
@ -16,11 +16,16 @@ import {
|
||||
UnsecureLoginArgs,
|
||||
UpdateUserInfosArgs,
|
||||
} from '../inputs/LoginUserInput'
|
||||
import { apiPost, apiGet } from '../../apis/loginAPI'
|
||||
|
||||
import { apiPost, apiGet } from '../../apis/HttpRequest'
|
||||
import {
|
||||
klicktippRegistrationMiddleware,
|
||||
klicktippNewsletterStateMiddleware,
|
||||
} from '../../middleware/klicktippMiddleware'
|
||||
import { CheckEmailResponse } from '../models/CheckEmailResponse'
|
||||
@Resolver()
|
||||
export class UserResolver {
|
||||
@Query(() => User)
|
||||
@UseMiddleware(klicktippNewsletterStateMiddleware)
|
||||
async login(@Args() { email, password }: UnsecureLoginArgs, @Ctx() context: any): Promise<User> {
|
||||
email = email.trim().toLowerCase()
|
||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password })
|
||||
@ -62,7 +67,9 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
@Query(() => String)
|
||||
async create(@Args() { email, firstName, lastName, password }: CreateUserArgs): Promise<string> {
|
||||
async create(
|
||||
@Args() { email, firstName, lastName, password, language }: CreateUserArgs,
|
||||
): Promise<string> {
|
||||
const payload = {
|
||||
email,
|
||||
first_name: firstName,
|
||||
@ -70,11 +77,14 @@ export class UserResolver {
|
||||
password,
|
||||
emailType: 2,
|
||||
login_after_register: true,
|
||||
language: language,
|
||||
publisher_id: 0,
|
||||
}
|
||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'createUser', payload)
|
||||
if (!result.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
|
||||
return 'success'
|
||||
}
|
||||
|
||||
@ -88,7 +98,9 @@ export class UserResolver {
|
||||
email_verification_code_type: 'resetPassword',
|
||||
}
|
||||
const response = await apiPost(CONFIG.LOGIN_API_URL + 'sendEmail', payload)
|
||||
if (!response.success) throw new Error(response.data)
|
||||
if (!response.success) {
|
||||
throw new Error(response.data)
|
||||
}
|
||||
return new SendPasswordResetEmailResponse(response.data)
|
||||
}
|
||||
|
||||
@ -103,8 +115,10 @@ export class UserResolver {
|
||||
password,
|
||||
}
|
||||
const result = await apiPost(CONFIG.LOGIN_API_URL + 'resetPassword', payload)
|
||||
if (!result.success) throw new Error(result.data)
|
||||
return 'sucess'
|
||||
if (!result.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
return 'success'
|
||||
}
|
||||
|
||||
@Authorized()
|
||||
@ -118,6 +132,7 @@ export class UserResolver {
|
||||
description,
|
||||
username,
|
||||
language,
|
||||
publisherId,
|
||||
password,
|
||||
passwordNew,
|
||||
}: UpdateUserInfosArgs,
|
||||
@ -132,6 +147,7 @@ export class UserResolver {
|
||||
'User.description': description || undefined,
|
||||
'User.username': username || undefined,
|
||||
'User.language': language || undefined,
|
||||
'User.publisher_id': publisherId || undefined,
|
||||
'User.password': passwordNew || undefined,
|
||||
'User.password_old': password || undefined,
|
||||
},
|
||||
@ -151,4 +167,16 @@ export class UserResolver {
|
||||
if (!response.success) throw new Error(response.data)
|
||||
return new CheckUsernameResponse(response.data)
|
||||
}
|
||||
|
||||
@Query(() => CheckEmailResponse)
|
||||
@UseMiddleware(klicktippRegistrationMiddleware)
|
||||
async checkEmail(@Arg('optin') optin: string): Promise<CheckEmailResponse> {
|
||||
const result = await apiGet(
|
||||
CONFIG.LOGIN_API_URL + 'loginViaEmailVerificationCode?emailVerificationCode=' + optin,
|
||||
)
|
||||
if (!result.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
return new CheckEmailResponse(result.data)
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import { UserResolver } from './graphql/resolvers/UserResolver'
|
||||
import { BalanceResolver } from './graphql/resolvers/BalanceResolver'
|
||||
import { GdtResolver } from './graphql/resolvers/GdtResolver'
|
||||
import { TransactionResolver } from './graphql/resolvers/TransactionResolver'
|
||||
import { KlicktippResolver } from './graphql/resolvers/KlicktippResolver'
|
||||
|
||||
import { isAuthorized } from './auth/auth'
|
||||
|
||||
@ -50,7 +51,7 @@ async function main() {
|
||||
|
||||
// const connection = await createConnection()
|
||||
const schema = await buildSchema({
|
||||
resolvers: [UserResolver, BalanceResolver, TransactionResolver, GdtResolver],
|
||||
resolvers: [UserResolver, BalanceResolver, TransactionResolver, GdtResolver, KlicktippResolver],
|
||||
authChecker: isAuthorized,
|
||||
})
|
||||
|
||||
|
||||
34
backend/src/middleware/klicktippMiddleware.ts
Normal file
34
backend/src/middleware/klicktippMiddleware.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { MiddlewareFn } from 'type-graphql'
|
||||
import { signIn, getKlickTippUser } from '../apis/KlicktippController'
|
||||
import { KlickTipp } from '../graphql/models/KlickTipp'
|
||||
import CONFIG from '../config/index'
|
||||
|
||||
export const klicktippRegistrationMiddleware: MiddlewareFn = async (
|
||||
// Only for demo
|
||||
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
||||
{ root, args, context, info },
|
||||
next,
|
||||
) => {
|
||||
// Do Something here before resolver is called
|
||||
const result = await next()
|
||||
// Do Something here after resolver is completed
|
||||
await signIn(result.email, result.language, result.firstName, result.lastName)
|
||||
return result
|
||||
}
|
||||
|
||||
export const klicktippNewsletterStateMiddleware: MiddlewareFn = async (
|
||||
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
||||
{ root, args, context, info },
|
||||
next,
|
||||
) => {
|
||||
const result = await next()
|
||||
let klickTipp = new KlickTipp({ status: 'Unsubscribed' })
|
||||
if (CONFIG.KLICKTIPP) {
|
||||
const klickTippUser = await getKlickTippUser(result.email)
|
||||
if (klickTippUser) {
|
||||
klickTipp = new KlickTipp(klickTippUser)
|
||||
}
|
||||
}
|
||||
result.klickTipp = klickTipp
|
||||
return result
|
||||
}
|
||||
0
backend/src/middleware/userResolverMiddleware.ts
Normal file
0
backend/src/middleware/userResolverMiddleware.ts
Normal file
@ -4,7 +4,7 @@
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
|
||||
28
docu/Concepts/Snippets/Authorization/concept.md
Normal file
28
docu/Concepts/Snippets/Authorization/concept.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Authorization and Private Keys
|
||||
## Keys
|
||||
For creating transactions ed25519 keys are used for signing.
|
||||
As long the user is the only controlling the private key he is the only one
|
||||
how can sign transactions on his behalf.
|
||||
It is a core concept of all crypto currencies and important for the concept,
|
||||
that the user has full control over his data.
|
||||
|
||||
Usually crypto currencies like bitcoin or iota save the keys on local system,
|
||||
maybe additional protected with a password which is used to encrypt the keys.
|
||||
|
||||
## Gradido
|
||||
Gradido should be easy to use, so we must offer a solution for everyone not that fit
|
||||
with computer, as easy to use like paypal.
|
||||
For that role we have the Login-Server.
|
||||
It stores the private keys of the user encrypted with there email and password.
|
||||
Additional it stores the passphrase which can be used to generate the private key,
|
||||
encryted with server admin public key. So only the server admin can access the keys
|
||||
with his private key. [not done yet]
|
||||
It is needed for passwort reset if a user has forgetten his password.
|
||||
|
||||
But for the entire concept Login-Server isn't the only way to store the private keys.
|
||||
For users which has more experience with computer and especially with crypto currencies
|
||||
it should be a way to keep there private keys by themselfs.
|
||||
|
||||
For example a Desktop- or Handy-App which store the keys locally maybe additional encrypted.
|
||||
Maybe it is possible to use Stronghold from iota for that.
|
||||
With that the user don't need to use the Login-Server.
|
||||
29
docu/Concepts/Snippets/Authorization/jwt.md
Normal file
29
docu/Concepts/Snippets/Authorization/jwt.md
Normal file
@ -0,0 +1,29 @@
|
||||
# How JWT could be used for authorization with and without Login-Server
|
||||
## What we need
|
||||
The only encrypted data in db are the private key.
|
||||
Every other data could be accessed without login, depending on frontend and backend code.
|
||||
So we need only a way to prove the backend that we have access to the private key.
|
||||
|
||||
## JWT
|
||||
JWT is perfect for that.
|
||||
We can use JWT to store the public key of the user as UUID for finding his data in db,
|
||||
signing it with the private key. So even if the backend is running in multiple instances,
|
||||
on every request is it possible to check the JWT token, that the signature is signed with
|
||||
the private key, belonging to the public key.
|
||||
The only thing the backend cannot do with that is signing a transaction.
|
||||
That can only be done by the Login-Server or a Desktop or Handy-App storing the private key locally.
|
||||
With that we have universal way for authorization against the backend.
|
||||
We could additional store if we like to sign transactions local or with Login-Server and the Login-Server url.
|
||||
|
||||
## JWT and Login-Server
|
||||
Login-Server uses Poco version 1.9.4 but unfortunately Poco only introduces jwt from version 1.10.
|
||||
And Updating to 1.10 needs some work because some things have changed in Poco 1.10.
|
||||
|
||||
## JWT signature algorithms
|
||||
In JWT standard ed25519 don't seemd to play a role.
|
||||
We must find out if we can use the ed25519 keys together with one of the signature algorithms
|
||||
in JWT standard or we must use **crypto_sign_verify_detached** from libsodium even it is nonstandard
|
||||
to verify signature created with ed25519 keys and libsodiums **crypto_sign_detached** function.
|
||||
|
||||
|
||||
|
||||
16
docu/Concepts/Snippets/Authorization/session_id.md
Normal file
16
docu/Concepts/Snippets/Authorization/session_id.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Session Id Authorization
|
||||
## Login-Server
|
||||
With every login, the Login-Server creates a session with a random id,
|
||||
storing it in memory. For Login email and password are needed.
|
||||
From email and an additional app-secret (**crypto.app_secret** in Login-Server config) a sha512 hash will be genereted, named **hash512_salt**.
|
||||
With sodium function *crypto_pwhash* with **hash512_salt** and user password a secret encryption key will be calculated.
|
||||
*crypto_pwhash* uses argon2 algorithmus to have a CPU hard calculation. Currently it is configured for < 0.5s.
|
||||
So it is harder to use brute-force attacks to guess the password. Even if someone gets hands on the data saved in db.
|
||||
|
||||
With sodium function *crypto_shorthash* a hash will be calculated from the secret encryption key and server crypto key (**crypto.server_key** in Login-Server config, hex encoded, 16 Bytes, 32 Character hex encoded)
|
||||
and compared against saved hash in db. If they identical user has successfull logged in.
|
||||
The secret encryption key will be stored in memory together with the user session and client ip from which login call came.
|
||||
The session_id will be returned.
|
||||
The session will be hold in memory for 15 minutes default, can be changed in Login-Server config field **session.timeout**
|
||||
|
||||
|
||||
@ -151,7 +151,7 @@ with:
|
||||
{
|
||||
"email":"max.musterman@gmail.de",
|
||||
"first_name":"Max",
|
||||
"last_name":"Musterman",
|
||||
"last_name":"Musterman" ,
|
||||
"username": "Maxilein",
|
||||
"description": "Tischler",
|
||||
"emailType": 2,
|
||||
@ -231,7 +231,8 @@ with:
|
||||
"User.disabled": 0,
|
||||
"User.language": "de",
|
||||
"User.password": "1234",
|
||||
"User.password_old": "4321"
|
||||
"User.password_old": "4321",
|
||||
"User.publisher_id": "1"
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -304,7 +305,8 @@ with:
|
||||
"user.description",
|
||||
"user.disabled",
|
||||
"user.email_checked",
|
||||
"user.language"
|
||||
"user.language",
|
||||
"user.publisher_id"
|
||||
]
|
||||
}
|
||||
```
|
||||
@ -342,6 +344,7 @@ Return only the fields which are defined in request
|
||||
is in db only saved in state_users so if we delete this entry, validating all transactions is no longer possible. Disabled User cannot login and cannot receive transactions.
|
||||
- `email_checked`: If user has clicked on link in verification email (register), can only transfer gradidos if email_checked is 1
|
||||
- `language`: Language Key for User, currently 'de' or 'en'
|
||||
- `publisher_id`: elopage publisher ip
|
||||
- `errors`: array of strings if error occure
|
||||
|
||||
## Login by Email Verification Code
|
||||
|
||||
BIN
docu/mock_choose_community.epgz
Normal file
BIN
docu/mock_choose_community.epgz
Normal file
Binary file not shown.
BIN
docu/mock_choose_community.png
Normal file
BIN
docu/mock_choose_community.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
@ -72,6 +72,9 @@ RUN yarn run build
|
||||
##################################################################################
|
||||
FROM build as test
|
||||
|
||||
# Install Additional Software
|
||||
RUN apk add --no-cache bash jq
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn run dev"
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bootstrap-vue-gradido-wallet",
|
||||
"version": "1.3.1",
|
||||
"version": "1.4.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node run/server.js",
|
||||
@ -9,7 +9,8 @@
|
||||
"lint": "eslint --ext .js,.vue .",
|
||||
"dev": "yarn run serve",
|
||||
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'",
|
||||
"test": "jest --coverage"
|
||||
"test": "jest --coverage",
|
||||
"locales": "scripts/missing-keys.sh && scripts/sort.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.13.13",
|
||||
@ -89,9 +90,9 @@
|
||||
"@vue/eslint-config-prettier": "^4.0.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-plugin-component": "^1.1.0",
|
||||
"dotenv-webpack": "^6.0.4",
|
||||
"node-sass": "^4.12.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"dotenv-webpack": "^7.0.3",
|
||||
"node-sass": "^6.0.1",
|
||||
"sass-loader": "^10",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"postcss": {
|
||||
|
||||
17
frontend/scripts/missing-keys.sh
Executable file
17
frontend/scripts/missing-keys.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
ROOT_DIR=$(dirname "$0")/..
|
||||
|
||||
sorting="jq -f $ROOT_DIR/scripts/sort_filter.jq"
|
||||
english="$sorting $ROOT_DIR/src/locales/en.json"
|
||||
german="$sorting $ROOT_DIR/src/locales/de.json"
|
||||
listPaths="jq -c 'path(..)|[.[]|tostring]|join(\".\")'"
|
||||
diffString="<( $english | $listPaths ) <( $german | $listPaths )"
|
||||
if eval "diff -q $diffString";
|
||||
then
|
||||
: # all good
|
||||
else
|
||||
eval "diff -y $diffString | grep '[|<>]'";
|
||||
printf "\nEnglish and German translation keys do not match, see diff above.\n"
|
||||
exit 1
|
||||
fi
|
||||
25
frontend/scripts/sort.sh
Executable file
25
frontend/scripts/sort.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
ROOT_DIR=$(dirname "$0")/..
|
||||
|
||||
tmp=$(mktemp)
|
||||
exit_code=0
|
||||
|
||||
for locale_file in $ROOT_DIR/src/locales/*.json
|
||||
do
|
||||
jq -f $(dirname "$0")/sort_filter.jq $locale_file > "$tmp"
|
||||
if [ "$*" == "--fix" ]
|
||||
then
|
||||
mv "$tmp" $locale_file
|
||||
else
|
||||
if diff -q "$tmp" $locale_file > /dev/null ;
|
||||
then
|
||||
: # all good
|
||||
else
|
||||
exit_code=$?
|
||||
echo "$(basename -- $locale_file) is not sorted by keys"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
exit $exit_code
|
||||
13
frontend/scripts/sort_filter.jq
Normal file
13
frontend/scripts/sort_filter.jq
Normal file
@ -0,0 +1,13 @@
|
||||
def walk(f):
|
||||
. as $in
|
||||
| if type == "object" then
|
||||
reduce keys_unsorted[] as $key
|
||||
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
|
||||
elif type == "array" then map( walk(f) ) | f
|
||||
else f
|
||||
end;
|
||||
|
||||
def keys_sort_by(f):
|
||||
to_entries | sort_by(.key|f ) | from_entries;
|
||||
|
||||
walk(if type == "object" then keys_sort_by(ascii_upcase) else . end)
|
||||
@ -2,29 +2,29 @@
|
||||
// Components
|
||||
//
|
||||
|
||||
@import "custom/alert";
|
||||
@import "custom/avatar";
|
||||
@import "custom/buttons";
|
||||
@import "custom/card";
|
||||
@import "custom/chart";
|
||||
@import "custom/close";
|
||||
@import "custom/content";
|
||||
@import "custom/custom-forms";
|
||||
@import "custom/dropdown";
|
||||
@import "custom/footer";
|
||||
@import "custom/forms";
|
||||
@import "custom/header";
|
||||
@import "custom/icons";
|
||||
@import "custom/input-group";
|
||||
@import "custom/list-group";
|
||||
@import "custom/map";
|
||||
@import "custom/mask";
|
||||
@import "custom/modal";
|
||||
@import "custom/nav";
|
||||
@import "custom/navbar";
|
||||
@import "custom/pagination";
|
||||
@import "custom/popover";
|
||||
@import "custom/progress";
|
||||
@import "custom/separator";
|
||||
@import "custom/tables";
|
||||
@import "custom/type";
|
||||
@import "alert";
|
||||
@import "avatar";
|
||||
@import "buttons";
|
||||
@import "card";
|
||||
@import "chart";
|
||||
@import "close";
|
||||
@import "content";
|
||||
@import "custom-forms";
|
||||
@import "dropdown";
|
||||
@import "footer";
|
||||
@import "forms";
|
||||
@import "header";
|
||||
@import "icons";
|
||||
@import "input-group";
|
||||
@import "list-group";
|
||||
@import "map";
|
||||
@import "mask";
|
||||
@import "modal";
|
||||
@import "nav";
|
||||
@import "navbar";
|
||||
@import "pagination";
|
||||
@import "popover";
|
||||
@import "progress";
|
||||
@import "separator";
|
||||
@import "tables";
|
||||
@import "type";
|
||||
|
||||
@ -1,79 +0,0 @@
|
||||
<template>
|
||||
<div id="accordion" role="tablist" aria-multiselectable="true" class="accordion">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'collapse',
|
||||
props: {
|
||||
animationDuration: {
|
||||
type: Number,
|
||||
default: 250,
|
||||
description: 'Collapse animation duration',
|
||||
},
|
||||
multipleActive: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
description: 'Whether you can have multiple collapse items opened at the same time',
|
||||
},
|
||||
activeIndex: {
|
||||
type: Number,
|
||||
default: -1,
|
||||
description: 'Active collapse item index',
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
animationDuration: this.animationDuration,
|
||||
multipleActive: this.multipleActive,
|
||||
addItem: this.addItem,
|
||||
removeItem: this.removeItem,
|
||||
deactivateAll: this.deactivateAll,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addItem(item) {
|
||||
const index = this.$slots.default.indexOf(item.$vnode)
|
||||
if (index !== -1) {
|
||||
this.items.splice(index, 0, item)
|
||||
}
|
||||
},
|
||||
removeItem(item) {
|
||||
const items = this.items
|
||||
const index = items.indexOf(item)
|
||||
if (index > -1) {
|
||||
items.splice(index, 1)
|
||||
}
|
||||
},
|
||||
deactivateAll() {
|
||||
this.items.forEach((item) => {
|
||||
item.active = false
|
||||
})
|
||||
},
|
||||
activateItem() {
|
||||
if (this.activeIndex !== -1) {
|
||||
this.items[this.activeIndex].active = true
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.activateItem()
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
activeIndex() {
|
||||
this.activateItem()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -1,91 +0,0 @@
|
||||
<template>
|
||||
<b-card no-body>
|
||||
<b-card-header role="tab" class="card-header" :aria-expanded="active">
|
||||
<a
|
||||
data-toggle="collapse"
|
||||
data-parent="#accordion"
|
||||
:href="`#${itemId}`"
|
||||
@click.prevent="activate"
|
||||
:aria-controls="`content-${itemId}`"
|
||||
>
|
||||
<slot name="title">{{ title }}</slot>
|
||||
<i class="tim-icons icon-minimal-down"></i>
|
||||
</a>
|
||||
</b-card-header>
|
||||
<collapse-transition :duration="animationDuration">
|
||||
<div
|
||||
v-show="active"
|
||||
:id="`content-${itemId}`"
|
||||
role="tabpanel"
|
||||
:aria-labelledby="title"
|
||||
class="collapsed"
|
||||
>
|
||||
<div class="card-body"><slot></slot></div>
|
||||
</div>
|
||||
</collapse-transition>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import { CollapseTransition } from 'vue2-transitions'
|
||||
|
||||
export default {
|
||||
name: 'collapse-item',
|
||||
components: {
|
||||
CollapseTransition,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
description: 'Collapse item title',
|
||||
},
|
||||
id: String,
|
||||
},
|
||||
inject: {
|
||||
animationDuration: {
|
||||
default: 250,
|
||||
},
|
||||
multipleActive: {
|
||||
default: false,
|
||||
},
|
||||
addItem: {
|
||||
default: () => {},
|
||||
},
|
||||
removeItem: {
|
||||
default: () => {},
|
||||
},
|
||||
deactivateAll: {
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
itemId() {
|
||||
return this.id || this.title
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
active: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
activate() {
|
||||
const wasActive = this.active
|
||||
if (!this.multipleActive) {
|
||||
this.deactivateAll()
|
||||
}
|
||||
this.active = !wasActive
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.addItem(this)
|
||||
},
|
||||
destroyed() {
|
||||
if (this.$el && this.$el.parentNode) {
|
||||
this.$el.parentNode.removeChild(this.$el)
|
||||
}
|
||||
this.removeItem(this)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
40
frontend/src/components/LanguageSwitchSelect.vue
Normal file
40
frontend/src/components/LanguageSwitchSelect.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="language-switch-select">
|
||||
<b-form-select
|
||||
v-model="selected"
|
||||
:options="options"
|
||||
class="selectedLanguage mb-3"
|
||||
></b-form-select>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'LanguageSwitch',
|
||||
data() {
|
||||
return {
|
||||
selected: null,
|
||||
options: [
|
||||
{ value: null, text: this.$t('select_language') },
|
||||
{ value: 'de', text: this.$t('languages.de') },
|
||||
{ value: 'en', text: this.$t('languages.en') },
|
||||
],
|
||||
}
|
||||
},
|
||||
props: {
|
||||
language: { type: String },
|
||||
},
|
||||
created() {
|
||||
this.selected = this.$store.state.language
|
||||
},
|
||||
computed: {
|
||||
languageObject() {
|
||||
return this.selected
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selected() {
|
||||
this.$emit('update-language', this.languageObject)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -3,11 +3,17 @@ import PaginationButtons from './PaginationButtons'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const propsData = {
|
||||
totalRows: 42,
|
||||
perPage: 12,
|
||||
value: 1,
|
||||
}
|
||||
|
||||
describe('PaginationButtons', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(PaginationButtons, { localVue })
|
||||
return mount(PaginationButtons, { localVue, propsData })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
@ -19,34 +25,20 @@ describe('PaginationButtons', () => {
|
||||
expect(wrapper.find('div.pagination-buttons').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has previous page button disabled by default', () => {
|
||||
expect(wrapper.find('button.previous-page').attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
|
||||
it('has bext page button disabled by default', () => {
|
||||
expect(wrapper.find('button.next-page').attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
|
||||
it('shows the text "1 / 1" by default"', () => {
|
||||
expect(wrapper.find('p.text-center').text()).toBe('1 / 1')
|
||||
})
|
||||
|
||||
describe('with active buttons', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setProps({
|
||||
hasNext: true,
|
||||
hasPrevious: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('emits show-previous when previous page button is clicked', () => {
|
||||
wrapper.find('button.previous-page').trigger('click')
|
||||
expect(wrapper.emitted('show-previous')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('emits show-next when next page button is clicked', () => {
|
||||
it('emits input next page button is clicked', async () => {
|
||||
wrapper.find('button.next-page').trigger('click')
|
||||
expect(wrapper.emitted('show-next')).toBeTruthy()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.emitted().input[0]).toEqual([2])
|
||||
})
|
||||
|
||||
it('emits input when previous page button is clicked', async () => {
|
||||
wrapper.setProps({ value: 2 })
|
||||
wrapper.setData({ currentValue: 2 })
|
||||
await wrapper.vm.$nextTick()
|
||||
wrapper.find('button.previous-page').trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.emitted().input[0]).toEqual([1])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div class="pagination-buttons">
|
||||
<div class="pagination-buttons" v-if="totalRows > perPage">
|
||||
<b-row class="m-4">
|
||||
<b-col class="text-right">
|
||||
<b-button class="previous-page" :disabled="!hasPrevious" @click="$emit('show-previous')">
|
||||
<b-button class="previous-page" :disabled="!hasPrevious" @click="currentValue--">
|
||||
<b-icon icon="chevron-left" variant="primary"></b-icon>
|
||||
</b-button>
|
||||
</b-col>
|
||||
<b-col cols="3">
|
||||
<p class="text-center pt-2">{{ currentPage }} / {{ totalPages }}</p>
|
||||
<p class="text-center pt-2">{{ value }} / {{ totalPages }}</p>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-button class="next-page" :disabled="!hasNext" @click="$emit('show-next')">
|
||||
<b-button class="next-page" :disabled="!hasNext" @click="currentValue++">
|
||||
<b-icon icon="chevron-right" variant="primary"></b-icon>
|
||||
</b-button>
|
||||
</b-col>
|
||||
@ -21,10 +21,33 @@
|
||||
export default {
|
||||
name: 'PaginationButtons',
|
||||
props: {
|
||||
hasNext: { type: Boolean, default: false },
|
||||
hasPrevious: { type: Boolean, default: false },
|
||||
totalPages: { type: Number, default: 1 },
|
||||
currentPage: { type: Number, default: 1 },
|
||||
totalRows: { required: true },
|
||||
perPage: { type: Number, required: true },
|
||||
value: { type: Number, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentValue: { type: Number, default: 1 },
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasNext() {
|
||||
return this.value * this.perPage < this.totalRows
|
||||
},
|
||||
hasPrevious() {
|
||||
return this.value > 1
|
||||
},
|
||||
totalPages() {
|
||||
return Math.ceil(this.totalRows / this.perPage)
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.currentValue = this.value
|
||||
},
|
||||
watch: {
|
||||
currentValue() {
|
||||
if (this.currentValue !== this.value) this.$emit('input', this.currentValue)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -113,12 +113,6 @@ describe('SideBar', () => {
|
||||
expect(wrapper.emitted('logout')).toEqual([[]])
|
||||
})
|
||||
})
|
||||
|
||||
describe('language-switch', () => {
|
||||
it('has a language-switch button', () => {
|
||||
expect(wrapper.find('div.language-switch').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -64,23 +64,18 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mt-5 ml-4">
|
||||
<language-switch />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
<script>
|
||||
import NavbarToggleButton from '@/components/NavbarToggleButton'
|
||||
import LanguageSwitch from '@/components/LanguageSwitch.vue'
|
||||
import VueQrcode from 'vue-qrcode'
|
||||
|
||||
export default {
|
||||
name: 'sidebar',
|
||||
components: {
|
||||
NavbarToggleButton,
|
||||
LanguageSwitch,
|
||||
VueQrcode,
|
||||
},
|
||||
props: {
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
import NavbarToggleButton from './Navbar/NavbarToggleButton'
|
||||
|
||||
import Collapse from './Collapse/Collapse.vue'
|
||||
import CollapseItem from './Collapse/CollapseItem.vue'
|
||||
|
||||
import SidebarPlugin from './SidebarPlugin'
|
||||
|
||||
export { SidebarPlugin, NavbarToggleButton, Collapse, CollapseItem }
|
||||
export { SidebarPlugin, NavbarToggleButton }
|
||||
|
||||
13
frontend/src/graphql/mutations.js
Normal file
13
frontend/src/graphql/mutations.js
Normal file
@ -0,0 +1,13 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const subscribeNewsletter = gql`
|
||||
mutation($email: String!, $language: String!) {
|
||||
subscribeNewsletter(email: $email, language: $language)
|
||||
}
|
||||
`
|
||||
|
||||
export const unsubscribeNewsletter = gql`
|
||||
mutation($email: String!) {
|
||||
unsubscribeNewsletter(email: $email)
|
||||
}
|
||||
`
|
||||
@ -9,6 +9,9 @@ export const login = gql`
|
||||
lastName
|
||||
language
|
||||
description
|
||||
klickTipp {
|
||||
newsletterState
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -92,8 +95,20 @@ export const transactionsQuery = gql`
|
||||
`
|
||||
|
||||
export const resgisterUserQuery = gql`
|
||||
query($firstName: String!, $lastName: String!, $email: String!, $password: String!) {
|
||||
create(email: $email, firstName: $firstName, lastName: $lastName, password: $password)
|
||||
query(
|
||||
$firstName: String!
|
||||
$lastName: String!
|
||||
$email: String!
|
||||
$password: String!
|
||||
$language: String!
|
||||
) {
|
||||
create(
|
||||
email: $email
|
||||
firstName: $firstName
|
||||
lastName: $lastName
|
||||
password: $password
|
||||
language: $language
|
||||
)
|
||||
}
|
||||
`
|
||||
|
||||
@ -136,3 +151,12 @@ export const listGDTEntriesQuery = gql`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const checkEmailQuery = gql`
|
||||
query($optin: String!) {
|
||||
checkEmail(optin: $optin) {
|
||||
email
|
||||
sessionId
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -1,181 +1,193 @@
|
||||
{
|
||||
"message": "hallo gradido !!",
|
||||
"welcome":"Willkommen!",
|
||||
"back": "Zurück",
|
||||
"community": "Gemeinschaft",
|
||||
"logout":"Abmelden",
|
||||
"login":"Anmeldung",
|
||||
"signup": "Registrieren",
|
||||
"reset": "Passwort zurücksetzen",
|
||||
"imprint":"Impressum",
|
||||
"privacy_policy":"Datenschutzerklärung",
|
||||
"members_area": "Mitgliederbereich",
|
||||
"whitepaper": "Whitepaper",
|
||||
"back":"Zurück",
|
||||
"send":"Senden",
|
||||
"transactions":"Transaktionen",
|
||||
"language":"Sprache",
|
||||
"languages":{
|
||||
"de": "Deutsch",
|
||||
"en": "English"
|
||||
"communitys": {
|
||||
"form": {
|
||||
"date_period": "Datum / Zeitraum",
|
||||
"hours": "Stunden",
|
||||
"hours_report": "Stundenbericht",
|
||||
"more_hours": "weitere Stunden",
|
||||
"submit": "Einreichen"
|
||||
}
|
||||
},
|
||||
"select_language": "Bitte wähle eine Sprache für die App und Newsletter",
|
||||
"decay": {
|
||||
"calculation_decay": "Berechnung der Vergänglichkeit",
|
||||
"created": "Geschöpft",
|
||||
"days": "Tage",
|
||||
"decay": "Vergänglichkeit",
|
||||
"decay_since_last_transaction":"Vergänglichkeit seit der letzten Transaktion",
|
||||
"calculation_decay":"Berechnung der Vergänglichkeit",
|
||||
"Starting_block_decay":"Startblock Vergänglichkeit",
|
||||
"decay_introduced":"Die Vergänglichkeit wurde Eingeführt am ",
|
||||
"decayStart": " - Startblock für Vergänglichkeit am: ",
|
||||
"last_transaction":"Letzte Transaktion",
|
||||
"past_time":"Vergangene Zeit",
|
||||
"since_introduction":"seit Einführung der Vergänglichkeit",
|
||||
"year":"Jahre",
|
||||
"months":"Monate",
|
||||
"days":"Tage",
|
||||
"hours":"Stunden",
|
||||
"minutes":"Minuten",
|
||||
"seconds":"Sekunden",
|
||||
"received":"Empfangen",
|
||||
"sent":"Gesendet",
|
||||
"created":"Geschöpft",
|
||||
"fromCommunity":"Aus der Gemeinschaft",
|
||||
"toCommunity":"An die Gemeinschaft",
|
||||
"noDecay": "Keine Vergänglichkeit"
|
||||
},
|
||||
"decay_introduced": "Die Vergänglichkeit wurde Eingeführt am ",
|
||||
"decay_since_last_transaction": "Vergänglichkeit seit der letzten Transaktion",
|
||||
"fromCommunity": "Aus der Gemeinschaft",
|
||||
"hours": "Stunden",
|
||||
"last_transaction": "Letzte Transaktion",
|
||||
"minutes": "Minuten",
|
||||
"months": "Monate",
|
||||
"noDecay": "Keine Vergänglichkeit",
|
||||
"past_time": "Vergangene Zeit",
|
||||
"received": "Empfangen",
|
||||
"seconds": "Sekunden",
|
||||
"sent": "Gesendet",
|
||||
"since_introduction": "seit Einführung der Vergänglichkeit",
|
||||
"Starting_block_decay": "Startblock Vergänglichkeit",
|
||||
"toCommunity": "An die Gemeinschaft",
|
||||
"year": "Jahre"
|
||||
},
|
||||
"error": {
|
||||
"change-password": "Fehler beim Ändern des Passworts",
|
||||
"error": "Fehler",
|
||||
"no-account": "Leider konnten wir keinen Account finden mit diesen Daten!"
|
||||
},
|
||||
"form": {
|
||||
"amount": "Betrag",
|
||||
"at": "am",
|
||||
"cancel": "Abbrechen",
|
||||
"reset": "Zurücksetzen",
|
||||
"close": "schließen",
|
||||
"edit": "bearbeiten",
|
||||
"save": "speichern",
|
||||
"recipient":"Empfänger",
|
||||
"sender":"Absender",
|
||||
"username":"Username",
|
||||
"firstname":"Vorname",
|
||||
"lastname":"Nachname",
|
||||
"description": "Beschreibung",
|
||||
"email":"E-Mail",
|
||||
"email_repeat":"eMail wiederholen",
|
||||
"password":"Passwort",
|
||||
"passwordRepeat":"Passwort wiederholen",
|
||||
"password_old":"altes Passwort",
|
||||
"password_new":"neues Passwort",
|
||||
"password_new_repeat":"neues Passwort wiederholen",
|
||||
"change": "ändern",
|
||||
"change-password": "Passwort ändern",
|
||||
"change-name": "Name ändern",
|
||||
"amount":"Betrag",
|
||||
"memo":"Nachricht",
|
||||
"message":"Nachricht",
|
||||
"date":"Datum",
|
||||
"from":"von",
|
||||
"to":"bis",
|
||||
"to1":"an",
|
||||
"at":"am",
|
||||
"time":"Zeit",
|
||||
"send_now":"Jetzt senden",
|
||||
"scann_code":"<strong>QR Code Scanner</strong> - Scanne den QR Code deines Partners",
|
||||
"max_gdd_info":"Maximale anzahl GDD zum versenden erreicht!",
|
||||
"send_check":"Bestätige deine Zahlung. Prüfe bitte nochmal alle Daten!",
|
||||
"thx":"Danke",
|
||||
"sorry":"Entschuldigung",
|
||||
"send_transaction_success":"Deine Transaktion wurde erfolgreich ausgeführt",
|
||||
"send_transaction_error":"Leider konnte die Transaktion nicht ausgeführt werden!",
|
||||
"change-password": "Passwort ändern",
|
||||
"changeLanguage": "Sprache ändern",
|
||||
"change_username_info": "Einmal gespeichert, kann der Username ncht mehr geändert werden!",
|
||||
"close": "schließen",
|
||||
"date": "Datum",
|
||||
"description": "Beschreibung",
|
||||
"edit": "bearbeiten",
|
||||
"email": "E-Mail",
|
||||
"email_repeat": "eMail wiederholen",
|
||||
"firstname": "Vorname",
|
||||
"from": "von",
|
||||
"lastname": "Nachname",
|
||||
"max_gdd_info": "Maximale anzahl GDD zum versenden erreicht!",
|
||||
"memo": "Nachricht",
|
||||
"message": "Nachricht",
|
||||
"password": "Passwort",
|
||||
"passwordRepeat": "Passwort wiederholen",
|
||||
"password_new": "neues Passwort",
|
||||
"password_new_repeat": "neues Passwort wiederholen",
|
||||
"password_old": "altes Passwort",
|
||||
"recipient": "Empfänger",
|
||||
"reset": "Zurücksetzen",
|
||||
"save": "speichern",
|
||||
"scann_code": "<strong>QR Code Scanner</strong> - Scanne den QR Code deines Partners",
|
||||
"sender": "Absender",
|
||||
"send_check": "Bestätige deine Zahlung. Prüfe bitte nochmal alle Daten!",
|
||||
"send_now": "Jetzt senden",
|
||||
"send_transaction_error": "Leider konnte die Transaktion nicht ausgeführt werden!",
|
||||
"send_transaction_success": "Deine Transaktion wurde erfolgreich ausgeführt",
|
||||
"sorry": "Entschuldigung",
|
||||
"thx": "Danke",
|
||||
"time": "Zeit",
|
||||
"to": "bis",
|
||||
"to1": "an",
|
||||
"username": "Username",
|
||||
"validation": {
|
||||
"gddSendAmount": "Das Feld {_field_} muss eine Zahl zwischen {min} und {max} mit höchstens zwei Nachkommastellen sein",
|
||||
"is-not": "Du kannst dir selbst keine Gradidos überweisen",
|
||||
"usernmae-unique": "Der Username ist bereits vergeben.",
|
||||
"usernmae-regex": "Der Username muss mit einem Buchstaben beginnen auf den mindestens zwei alfanumerische Zeichen folgen müssen."
|
||||
},
|
||||
"change_username_info": "Einmal gespeichert, kann der Username ncht mehr geändert werden!"
|
||||
},
|
||||
"error": {
|
||||
"error":"Fehler",
|
||||
"no-account": "Leider konnten wir keinen Account finden mit diesen Daten!",
|
||||
"change-password": "Fehler beim Ändern des Passworts"
|
||||
},
|
||||
"transaction":{
|
||||
"show_all":"Alle <strong>{count}</strong> Transaktionen ansehen",
|
||||
"nullTransactions":"Du hast noch keine Transaktionen auf deinem Konto.",
|
||||
"more": "mehr",
|
||||
"receiverNotFound":"Empfänger nicht gefunden",
|
||||
"gdd-text":"Gradido Transaktionen",
|
||||
"gdt-text":"Gradido Transform Transaktionen"
|
||||
},
|
||||
"site": {
|
||||
"login": {
|
||||
"community":"Tausend Dank, weil du bei uns bist!",
|
||||
"remember":"Passwort merken",
|
||||
"signin":"Anmelden",
|
||||
"forgot_pwd":"Passwort vergessen?",
|
||||
"new_wallet":"Neues Konto erstellen"
|
||||
},
|
||||
"signup": {
|
||||
"title": "Erstelle dein Gradido-Konto",
|
||||
"subtitle": "Werde Teil der Gemeinschaft!",
|
||||
"agree":"Ich stimme der <a href='https://gradido.net/de/datenschutz/' target='_blank' >Datenschutzerklärung</a> zu.",
|
||||
"lowercase":"Ein Kleinbuchstabe erforderlich.",
|
||||
"uppercase":"Ein Großbuchstabe erforderlich.",
|
||||
"minimum":"Mindestens 8 Zeichen.",
|
||||
"one_number":"Eine Zahl erforderlich.",
|
||||
"dont_match":"Die Passwörter stimmen nicht überein."
|
||||
},
|
||||
"password": {
|
||||
"title": "Passwort zurücksetzen",
|
||||
"subtitle": "Wenn du dein Passwort vergessen hast, kannst du es hier zurücksetzen.",
|
||||
"send_now": "Jetzt senden"
|
||||
},
|
||||
"thx": {
|
||||
"title": "Danke!",
|
||||
"email": "Wir haben dir eine eMail gesendet.",
|
||||
"reset": "Dein Passwort wurde geändert.",
|
||||
"register": "Du bist jetzt regisriert."
|
||||
},
|
||||
"overview":{
|
||||
"account_overview":"Kontoübersicht",
|
||||
"since_last_month": "seid letzten Monat",
|
||||
"send_gradido":"Gradido versenden",
|
||||
"add_work":"neuer Gemeinschaftsbeitrag"
|
||||
},
|
||||
"navbar" : {
|
||||
"my-profil":"Mein Profil",
|
||||
"settings":"Einstellungen",
|
||||
"activity":"Aktivität",
|
||||
"support":"Support"
|
||||
},
|
||||
"404" : {
|
||||
"ooops" : "Ooops!",
|
||||
"text" : "Seite nicht gefunden. Aber keine Sorge, wir haben noch viele andere Seiten zum Erkunden",
|
||||
"back" : "Zurück zur Übersicht!"
|
||||
"usernmae-regex": "Der Username muss mit einem Buchstaben beginnen auf den mindestens zwei alfanumerische Zeichen folgen müssen.",
|
||||
"usernmae-unique": "Der Username ist bereits vergeben."
|
||||
}
|
||||
},
|
||||
"communitys":{
|
||||
"form":{
|
||||
"hours":"Stunden",
|
||||
"date_period":"Datum / Zeitraum",
|
||||
"more_hours":"weitere Stunden",
|
||||
"submit":"Einreichen",
|
||||
"hours_report":"Stundenbericht"
|
||||
}
|
||||
},
|
||||
"reset-password": {
|
||||
"title": "Passwort zurücksetzen",
|
||||
"text": "Jetzt kannst du ein neues Passwort speichern, mit dem du dich zukünftig in der Gradido-App anmelden kannst.",
|
||||
"not-authenticated": "Leider konnten wir dich nicht authentifizieren. Bitte wende dich an den Support."
|
||||
},
|
||||
"gdt": {
|
||||
"gdt-received":"Gradido Transform (GDT) erhalten",
|
||||
"factor":"Faktor",
|
||||
"raise": "Erhöhung",
|
||||
"action": "Aktion",
|
||||
"calculation": "Berechnung der Gradido Transform",
|
||||
"contribution": "Beitrag",
|
||||
"conversion": "Umrechnung",
|
||||
"conversion-gdt-euro": "Umrechnung Euro / Gradido Transform (GDT)",
|
||||
"credit": "Gutschrift",
|
||||
"conversion-gdt-euro":"Umrechnung Euro / Gradido Transform (GDT)",
|
||||
"calculation":"Berechnung der Gradido Transform",
|
||||
"conversion":"Umrechnung",
|
||||
"formula":"Berechungsformel",
|
||||
"no-transactions":"Du hast zur Zeit keine Transaktionen",
|
||||
"publisher":"Dein geworbenes Mitglied hat einen Beitrag bezahlt",
|
||||
"action":"Aktion",
|
||||
"recruited-member":"Geworbenes Mitglied",
|
||||
"contribution":"Beitrag"
|
||||
}
|
||||
"factor": "Faktor",
|
||||
"formula": "Berechungsformel",
|
||||
"gdt-received": "Gradido Transform (GDT) erhalten",
|
||||
"no-transactions": "Du hast zur Zeit keine Transaktionen",
|
||||
"publisher": "Dein geworbenes Mitglied hat einen Beitrag bezahlt",
|
||||
"raise": "Erhöhung",
|
||||
"recruited-member": "Geworbenes Mitglied"
|
||||
},
|
||||
"imprint": "Impressum",
|
||||
"language": "Sprache",
|
||||
"languages": {
|
||||
"de": "Deutsch",
|
||||
"en": "English"
|
||||
},
|
||||
"login": "Anmeldung",
|
||||
"logout": "Abmelden",
|
||||
"members_area": "Mitgliederbereich",
|
||||
"message": "hallo gradido !!",
|
||||
"privacy_policy": "Datenschutzerklärung",
|
||||
"reset": "Passwort zurücksetzen",
|
||||
"reset-password": {
|
||||
"not-authenticated": "Leider konnten wir dich nicht authentifizieren. Bitte wende dich an den Support.",
|
||||
"text": "Jetzt kannst du ein neues Passwort speichern, mit dem du dich zukünftig in der Gradido-App anmelden kannst.",
|
||||
"title": "Passwort zurücksetzen"
|
||||
},
|
||||
"select_language": "Bitte wähle eine Sprache für die App und Newsletter",
|
||||
"send": "Senden",
|
||||
"setting": {
|
||||
"changeNewsletter": "Newsletter Status ändern",
|
||||
"newsletter": "Newsletter",
|
||||
"newsletterFalse": "Du bist aus Newslettersystem ausgetragen.",
|
||||
"newsletterTrue": "Du bist im Newslettersystem eingetraten."
|
||||
},
|
||||
"signup": "Registrieren",
|
||||
"site": {
|
||||
"404": {
|
||||
"back": "Zurück zur Übersicht!",
|
||||
"ooops": "Ooops!",
|
||||
"text": "Seite nicht gefunden. Aber keine Sorge, wir haben noch viele andere Seiten zum Erkunden"
|
||||
},
|
||||
"checkEmail": {
|
||||
"errorText": "Email konnte nicht verifiziert werden.",
|
||||
"title": "Email wird verifiziert"
|
||||
},
|
||||
"login": {
|
||||
"community": "Tausend Dank, weil du bei uns bist!",
|
||||
"forgot_pwd": "Passwort vergessen?",
|
||||
"new_wallet": "Neues Konto erstellen",
|
||||
"remember": "Passwort merken",
|
||||
"signin": "Anmelden"
|
||||
},
|
||||
"navbar": {
|
||||
"activity": "Aktivität",
|
||||
"my-profil": "Mein Profil",
|
||||
"settings": "Einstellungen",
|
||||
"support": "Support"
|
||||
},
|
||||
"overview": {
|
||||
"account_overview": "Kontoübersicht",
|
||||
"add_work": "neuer Gemeinschaftsbeitrag",
|
||||
"send_gradido": "Gradido versenden",
|
||||
"since_last_month": "seid letzten Monat"
|
||||
},
|
||||
"password": {
|
||||
"send_now": "Jetzt senden",
|
||||
"subtitle": "Wenn du dein Passwort vergessen hast, kannst du es hier zurücksetzen.",
|
||||
"title": "Passwort zurücksetzen"
|
||||
},
|
||||
"signup": {
|
||||
"agree": "Ich stimme der <a href='https://gradido.net/de/datenschutz/' target='_blank' >Datenschutzerklärung</a> zu.",
|
||||
"dont_match": "Die Passwörter stimmen nicht überein.",
|
||||
"lowercase": "Ein Kleinbuchstabe erforderlich.",
|
||||
"minimum": "Mindestens 8 Zeichen.",
|
||||
"one_number": "Eine Zahl erforderlich.",
|
||||
"subtitle": "Werde Teil der Gemeinschaft!",
|
||||
"title": "Erstelle dein Gradido-Konto",
|
||||
"uppercase": "Ein Großbuchstabe erforderlich."
|
||||
},
|
||||
"thx": {
|
||||
"checkEmail": "Deine Email würde erfolgreich verifiziert.",
|
||||
"email": "Wir haben dir eine eMail gesendet.",
|
||||
"register": "Du bist jetzt regisriert.",
|
||||
"reset": "Dein Passwort wurde geändert.",
|
||||
"title": "Danke!"
|
||||
}
|
||||
},
|
||||
"transaction": {
|
||||
"gdd-text": "Gradido Transaktionen",
|
||||
"gdt-text": "Gradido Transform Transaktionen",
|
||||
"more": "mehr",
|
||||
"nullTransactions": "Du hast noch keine Transaktionen auf deinem Konto.",
|
||||
"receiverNotFound": "Empfänger nicht gefunden",
|
||||
"show_all": "Alle <strong>{count}</strong> Transaktionen ansehen"
|
||||
},
|
||||
"transactions": "Transaktionen",
|
||||
"welcome": "Willkommen!",
|
||||
"whitepaper": "Whitepaper"
|
||||
}
|
||||
|
||||
@ -1,181 +1,193 @@
|
||||
{
|
||||
"message": "hello gradido !!",
|
||||
"welcome":"Welcome!",
|
||||
"back": "Back",
|
||||
"community": "Community",
|
||||
"logout":"Logout",
|
||||
"login":"Login",
|
||||
"signup": "Sign up",
|
||||
"reset": "Reset password",
|
||||
"imprint":"Legal notice",
|
||||
"privacy_policy":"Privacy policy",
|
||||
"members_area": "Member's area",
|
||||
"whitepaper": "Whitepaper",
|
||||
"back":"Back",
|
||||
"send":"Send",
|
||||
"transactions":"Transactions",
|
||||
"language":"Language",
|
||||
"languages":{
|
||||
"de": "Deutsch",
|
||||
"en": "English"
|
||||
"communitys": {
|
||||
"form": {
|
||||
"date_period": "Date / Period",
|
||||
"hours": "hours",
|
||||
"hours_report": "Hourly report",
|
||||
"more_hours": "more hours",
|
||||
"submit": "submit"
|
||||
}
|
||||
},
|
||||
"select_language": "Please choose a language for the app and newsletter",
|
||||
"decay": {
|
||||
"decay": "Decay",
|
||||
"decay_since_last_transaction":"Decay since the last transaction",
|
||||
"calculation_decay": "Calculation of Decay",
|
||||
"Starting_block_decay": "Starting Block Decay",
|
||||
"decay_introduced": "Decay was Introduced on",
|
||||
"decayStart": " - Starting block for decay at: ",
|
||||
"last_transaction": "Last transaction:",
|
||||
"past_time": "Past time",
|
||||
"since_introduction": "Since the introduction of Decay",
|
||||
"year": "Years",
|
||||
"months": "Months",
|
||||
"created": "Created",
|
||||
"days": "Days",
|
||||
"decay": "Decay",
|
||||
"decayStart": " - Starting block for decay at: ",
|
||||
"decay_introduced": "Decay was Introduced on",
|
||||
"decay_since_last_transaction": "Decay since the last transaction",
|
||||
"fromCommunity": "From the community",
|
||||
"hours": "Hours",
|
||||
"last_transaction": "Last transaction:",
|
||||
"minutes": "Minutes",
|
||||
"months": "Months",
|
||||
"noDecay": "No Decay",
|
||||
"past_time": "Past time",
|
||||
"received": "Received",
|
||||
"seconds": "Seconds",
|
||||
"received":"Received",
|
||||
"sent":"Sent",
|
||||
"created":"Created",
|
||||
"fromCommunity":"From the community",
|
||||
"toCommunity":"To the community",
|
||||
"noDecay": "No Decay"
|
||||
"sent": "Sent",
|
||||
"since_introduction": "Since the introduction of Decay",
|
||||
"Starting_block_decay": "Starting Block Decay",
|
||||
"toCommunity": "To the community",
|
||||
"year": "Years"
|
||||
},
|
||||
"error": {
|
||||
"change-password": "Error while changing password",
|
||||
"error": "Error",
|
||||
"no-account": "Unfortunately we could not find an account to the given data!"
|
||||
},
|
||||
"form": {
|
||||
"cancel":"Cancel",
|
||||
"reset": "Reset",
|
||||
"close":"Close",
|
||||
"edit": "Edit",
|
||||
"save": "save",
|
||||
"recipient":"Recipient",
|
||||
"sender":"Sender",
|
||||
"username":"Username",
|
||||
"firstname":"Firstname",
|
||||
"lastname":"Lastname",
|
||||
"description": "Description",
|
||||
"email":"Email",
|
||||
"email_repeat":"Repeat Email",
|
||||
"password":"Password",
|
||||
"passwordRepeat":"Repeat password",
|
||||
"password_old":"Old password",
|
||||
"password_new":"New password",
|
||||
"password_new_repeat":"Repeat new password",
|
||||
"amount": "Amount",
|
||||
"at": "at",
|
||||
"cancel": "Cancel",
|
||||
"change": "change",
|
||||
"change-password": "Change password",
|
||||
"change-name": "Change name",
|
||||
"amount":"Amount",
|
||||
"memo":"Message",
|
||||
"message":"Message",
|
||||
"date":"Date",
|
||||
"from":"from",
|
||||
"to":"to",
|
||||
"to1":"to",
|
||||
"at":"at",
|
||||
"time":"Time",
|
||||
"send_now":"Send now",
|
||||
"scann_code":"<strong>QR Code Scanner</strong> - Scan the QR Code of your partner",
|
||||
"max_gdd_info":"Maximum number of GDDs to be sent has been reached!",
|
||||
"send_check":"Confirm your payment. Please check all data again!",
|
||||
"thx":"Thank you",
|
||||
"sorry":"Sorry",
|
||||
"send_transaction_success":"Your transaction was successfully completed",
|
||||
"send_transaction_error":"Unfortunately, the transaction could not be executed!",
|
||||
"change-password": "Change password",
|
||||
"changeLanguage": "Change language",
|
||||
"change_username_info": "Once saved, the username cannot be changed again!",
|
||||
"close": "Close",
|
||||
"date": "Date",
|
||||
"description": "Description",
|
||||
"edit": "Edit",
|
||||
"email": "Email",
|
||||
"email_repeat": "Repeat Email",
|
||||
"firstname": "Firstname",
|
||||
"from": "from",
|
||||
"lastname": "Lastname",
|
||||
"max_gdd_info": "Maximum number of GDDs to be sent has been reached!",
|
||||
"memo": "Message",
|
||||
"message": "Message",
|
||||
"password": "Password",
|
||||
"passwordRepeat": "Repeat password",
|
||||
"password_new": "New password",
|
||||
"password_new_repeat": "Repeat new password",
|
||||
"password_old": "Old password",
|
||||
"recipient": "Recipient",
|
||||
"reset": "Reset",
|
||||
"save": "save",
|
||||
"scann_code": "<strong>QR Code Scanner</strong> - Scan the QR Code of your partner",
|
||||
"sender": "Sender",
|
||||
"send_check": "Confirm your payment. Please check all data again!",
|
||||
"send_now": "Send now",
|
||||
"send_transaction_error": "Unfortunately, the transaction could not be executed!",
|
||||
"send_transaction_success": "Your transaction was successfully completed",
|
||||
"sorry": "Sorry",
|
||||
"thx": "Thank you",
|
||||
"time": "Time",
|
||||
"to": "to",
|
||||
"to1": "to",
|
||||
"username": "Username",
|
||||
"validation": {
|
||||
"gddSendAmount": "The {_field_} field must be a number between {min} and {max} with at most two digits",
|
||||
"is-not": "You cannot send Gradidos to yourself",
|
||||
"usernmae-unique": "The username is already taken.",
|
||||
"usernmae-regex": "The username must start with a letter, followed by at least two alphanumeric characters."
|
||||
},
|
||||
"change_username_info": "Once saved, the username cannot be changed again!"
|
||||
},
|
||||
"error": {
|
||||
"error":"Error",
|
||||
"no-account": "Unfortunately we could not find an account to the given data!",
|
||||
"change-password": "Error while changing password"
|
||||
},
|
||||
"transaction":{
|
||||
"show_all":"View all <strong>{count}</strong> transactions.",
|
||||
"nullTransactions":"You don't have any transactions on your account yet.",
|
||||
"more": "more",
|
||||
"receiverNotFound":"Recipient not found",
|
||||
"gdd-text":"Gradido Transactions",
|
||||
"gdt-text":"Gradido Transform Transactions"
|
||||
},
|
||||
"site": {
|
||||
"login": {
|
||||
"community":"A thousand thanks for being with us!",
|
||||
"remember":"Remember password",
|
||||
"signin":"Sign in",
|
||||
"forgot_pwd":"Forgot password?",
|
||||
"new_wallet":"Create new account"
|
||||
},
|
||||
"signup": {
|
||||
"title": "Create your Gradido account",
|
||||
"subtitle": "Become a part of the community!",
|
||||
"agree":"I agree to the <a href='https://gradido.net/en/datenschutz/' target='_blank' > privacy policy</a>.",
|
||||
"lowercase":"One lowercase letter required.",
|
||||
"uppercase":"One uppercase letter required.",
|
||||
"minimum":"8 characters minimum.",
|
||||
"one_number":"One number required.",
|
||||
"dont_match":"Passwords don't match."
|
||||
},
|
||||
"password": {
|
||||
"title": "Reset password",
|
||||
"subtitle": "If you have forgotten your password, you can reset it here.",
|
||||
"send_now": "Send now"
|
||||
},
|
||||
"thx": {
|
||||
"title": "Thank you!",
|
||||
"email": "We have sent you an email.",
|
||||
"reset": "Your password has been changed.",
|
||||
"register": "You are registred now."
|
||||
},
|
||||
"overview":{
|
||||
"account_overview":"Account overview",
|
||||
"since_last_month": "since last month",
|
||||
"send_gradido":"Send Gradido",
|
||||
"add_work":"New Community Contribution"
|
||||
},
|
||||
"navbar" : {
|
||||
"my-profil":"My profile",
|
||||
"settings":"Settings",
|
||||
"activity":"Activity",
|
||||
"support":"Support"
|
||||
},
|
||||
"404" : {
|
||||
"ooops" : "Ooops!",
|
||||
"text" : "Page not found. Do not worry though, we have plenty of other pages to explore",
|
||||
"back" : "Back to dashboard!"
|
||||
}
|
||||
},
|
||||
"communitys":{
|
||||
"form":{
|
||||
"hours":"hours",
|
||||
"date_period":"Date / Period",
|
||||
"more_hours":"more hours",
|
||||
"submit":"submit",
|
||||
"hours_report":"Hourly report"
|
||||
"usernmae-regex": "The username must start with a letter, followed by at least two alphanumeric characters.",
|
||||
"usernmae-unique": "The username is already taken."
|
||||
}
|
||||
},
|
||||
"reset-password": {
|
||||
"title": "Reset Password",
|
||||
"text": "Now you can save a new password to login to the Gradido-App in the future.",
|
||||
"not-authenticated": "Unfortunately we could not authenticate you. Please contact the support."
|
||||
},
|
||||
"gdt": {
|
||||
"gdt-received": "Gradido Transform (GDT) received",
|
||||
"factor": "Factor",
|
||||
"raise": "Increase",
|
||||
"credit": "Credit",
|
||||
"conversion-gdt-euro": "Conversion Euro / Gradido Transform (GDT)",
|
||||
"action": "Action",
|
||||
"calculation": "Calculation of Gradido Transform",
|
||||
"contribution": "Contribution",
|
||||
"conversion": "Conversion",
|
||||
"conversion-gdt-euro": "Conversion Euro / Gradido Transform (GDT)",
|
||||
"credit": "Credit",
|
||||
"factor": "Factor",
|
||||
"formula": "Calculation formula",
|
||||
"no-transactions":"You currently have no transactions",
|
||||
"publisher":"A member you referred has paid a contribution",
|
||||
"action":"Action",
|
||||
"recruited-member":"Recruited Member",
|
||||
"contribution":"Contribution"
|
||||
}
|
||||
"gdt-received": "Gradido Transform (GDT) received",
|
||||
"no-transactions": "You currently have no transactions",
|
||||
"publisher": "A member you referred has paid a contribution",
|
||||
"raise": "Increase",
|
||||
"recruited-member": "Recruited Member"
|
||||
},
|
||||
"imprint": "Legal notice",
|
||||
"language": "Language",
|
||||
"languages": {
|
||||
"de": "Deutsch",
|
||||
"en": "English"
|
||||
},
|
||||
"login": "Login",
|
||||
"logout": "Logout",
|
||||
"members_area": "Member's area",
|
||||
"message": "hello gradido !!",
|
||||
"privacy_policy": "Privacy policy",
|
||||
"reset": "Reset password",
|
||||
"reset-password": {
|
||||
"not-authenticated": "Unfortunately we could not authenticate you. Please contact the support.",
|
||||
"text": "Now you can save a new password to login to the Gradido-App in the future.",
|
||||
"title": "Reset Password"
|
||||
},
|
||||
"select_language": "Please choose a language for the app and newsletter",
|
||||
"send": "Send",
|
||||
"setting": {
|
||||
"changeNewsletter": "Newsletter status change",
|
||||
"newsletter": "Newsletter",
|
||||
"newsletterFalse": "You are unsubscribed from newsletter system.",
|
||||
"newsletterTrue": "You are subscribed to newsletter system."
|
||||
},
|
||||
"signup": "Sign up",
|
||||
"site": {
|
||||
"404": {
|
||||
"back": "Back to dashboard!",
|
||||
"ooops": "Ooops!",
|
||||
"text": "Page not found. Do not worry though, we have plenty of other pages to explore"
|
||||
},
|
||||
"checkEmail": {
|
||||
"errorText": "Could not verify the email.",
|
||||
"title": "Verifing email"
|
||||
},
|
||||
"login": {
|
||||
"community": "A thousand thanks for being with us!",
|
||||
"forgot_pwd": "Forgot password?",
|
||||
"new_wallet": "Create new account",
|
||||
"remember": "Remember password",
|
||||
"signin": "Sign in"
|
||||
},
|
||||
"navbar": {
|
||||
"activity": "Activity",
|
||||
"my-profil": "My profile",
|
||||
"settings": "Settings",
|
||||
"support": "Support"
|
||||
},
|
||||
"overview": {
|
||||
"account_overview": "Account overview",
|
||||
"add_work": "New Community Contribution",
|
||||
"send_gradido": "Send Gradido",
|
||||
"since_last_month": "since last month"
|
||||
},
|
||||
"password": {
|
||||
"send_now": "Send now",
|
||||
"subtitle": "If you have forgotten your password, you can reset it here.",
|
||||
"title": "Reset password"
|
||||
},
|
||||
"signup": {
|
||||
"agree": "I agree to the <a href='https://gradido.net/en/datenschutz/' target='_blank' > privacy policy</a>.",
|
||||
"dont_match": "Passwords don't match.",
|
||||
"lowercase": "One lowercase letter required.",
|
||||
"minimum": "8 characters minimum.",
|
||||
"one_number": "One number required.",
|
||||
"subtitle": "Become a part of the community!",
|
||||
"title": "Create your Gradido account",
|
||||
"uppercase": "One uppercase letter required."
|
||||
},
|
||||
"thx": {
|
||||
"checkEmail": "Your email has been successfully verified.",
|
||||
"email": "We have sent you an email.",
|
||||
"register": "You are registred now.",
|
||||
"reset": "Your password has been changed.",
|
||||
"title": "Thank you!"
|
||||
}
|
||||
},
|
||||
"transaction": {
|
||||
"gdd-text": "Gradido Transactions",
|
||||
"gdt-text": "Gradido Transform Transactions",
|
||||
"more": "more",
|
||||
"nullTransactions": "You don't have any transactions on your account yet.",
|
||||
"receiverNotFound": "Recipient not found",
|
||||
"show_all": "View all <strong>{count}</strong> transactions."
|
||||
},
|
||||
"transactions": "Transactions",
|
||||
"welcome": "Welcome!",
|
||||
"whitepaper": "Whitepaper"
|
||||
}
|
||||
|
||||
@ -52,6 +52,10 @@ const routes = [
|
||||
path: '/reset/:optin',
|
||||
component: () => import('../views/Pages/ResetPassword.vue'),
|
||||
},
|
||||
{
|
||||
path: '/checkEmail/:optin',
|
||||
component: () => import('../views/Pages/CheckEmail.vue'),
|
||||
},
|
||||
{ path: '*', component: NotFound },
|
||||
]
|
||||
|
||||
|
||||
@ -26,6 +26,9 @@ export const mutations = {
|
||||
token: (state, token) => {
|
||||
state.token = token
|
||||
},
|
||||
newsletterState: (state, newsletterState) => {
|
||||
state.newsletterState = newsletterState
|
||||
},
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
@ -36,6 +39,7 @@ export const actions = {
|
||||
commit('firstName', data.firstName)
|
||||
commit('lastName', data.lastName)
|
||||
commit('description', data.description)
|
||||
commit('newsletterState', data.klickTipp.newsletterState)
|
||||
},
|
||||
logout: ({ commit, state }) => {
|
||||
commit('token', null)
|
||||
@ -44,6 +48,7 @@ export const actions = {
|
||||
commit('firstName', '')
|
||||
commit('lastName', '')
|
||||
commit('description', '')
|
||||
commit('newsletterState', null)
|
||||
localStorage.clear()
|
||||
},
|
||||
}
|
||||
@ -62,6 +67,7 @@ export const store = new Vuex.Store({
|
||||
username: '',
|
||||
description: '',
|
||||
token: null,
|
||||
newsletterState: null,
|
||||
},
|
||||
getters: {},
|
||||
// Syncronous mutation of the state
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
import { mutations, actions } from './store'
|
||||
|
||||
const { language, email, token, username, firstName, lastName, description } = mutations
|
||||
const {
|
||||
language,
|
||||
email,
|
||||
token,
|
||||
username,
|
||||
firstName,
|
||||
lastName,
|
||||
description,
|
||||
newsletterState,
|
||||
} = mutations
|
||||
const { login, logout } = actions
|
||||
|
||||
describe('Vuex store', () => {
|
||||
@ -60,6 +69,14 @@ describe('Vuex store', () => {
|
||||
expect(state.description).toEqual('Nickelbrille')
|
||||
})
|
||||
})
|
||||
|
||||
describe('newsletterState', () => {
|
||||
it('sets the state of newsletterState', () => {
|
||||
const state = { newsletterState: null }
|
||||
newsletterState(state, true)
|
||||
expect(state.newsletterState).toEqual(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('actions', () => {
|
||||
@ -73,11 +90,14 @@ describe('Vuex store', () => {
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
description: 'Nickelbrille',
|
||||
klickTipp: {
|
||||
newsletterState: true,
|
||||
},
|
||||
}
|
||||
|
||||
it('calls seven commits', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenCalledTimes(6)
|
||||
expect(commit).toHaveBeenCalledTimes(7)
|
||||
})
|
||||
|
||||
it('commits email', () => {
|
||||
@ -109,6 +129,11 @@ describe('Vuex store', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(6, 'description', 'Nickelbrille')
|
||||
})
|
||||
|
||||
it('commits newsletterState', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(7, 'newsletterState', true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('logout', () => {
|
||||
@ -117,7 +142,7 @@ describe('Vuex store', () => {
|
||||
|
||||
it('calls six commits', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenCalledTimes(6)
|
||||
expect(commit).toHaveBeenCalledTimes(7)
|
||||
})
|
||||
|
||||
it('commits token', () => {
|
||||
@ -150,6 +175,11 @@ describe('Vuex store', () => {
|
||||
expect(commit).toHaveBeenNthCalledWith(6, 'description', '')
|
||||
})
|
||||
|
||||
it('commits newsletterState', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(7, 'newsletterState', null)
|
||||
})
|
||||
|
||||
// how to get this working?
|
||||
it.skip('calls localStorage.clear()', () => {
|
||||
const clearStorageMock = jest.fn()
|
||||
|
||||
@ -117,10 +117,6 @@ describe('DashboardLayoutGdd', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('has a locale switch', () => {
|
||||
expect(wrapper.find('div.language-switch').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has a logout button', () => {
|
||||
expect(wrapper.findAll('ul').at(3).text()).toBe('logout')
|
||||
})
|
||||
|
||||
@ -6,6 +6,8 @@ sendMock.mockResolvedValue('success')
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
window.scrollTo = jest.fn()
|
||||
|
||||
describe('AccountOverview', () => {
|
||||
let wrapper
|
||||
|
||||
|
||||
@ -1,183 +0,0 @@
|
||||
<template>
|
||||
<div class="pt-4 pb-4">
|
||||
<b-tabs content-class="mt-3" class="display-4" fill>
|
||||
<b-tab :title="names.thisMonth" active>
|
||||
<b-row>
|
||||
<b-col lg="3">
|
||||
<b-input :label="$t('communitys.form.hours')">
|
||||
<b-form-input
|
||||
type="number"
|
||||
size="lg"
|
||||
placeholder="23"
|
||||
style="font-size: xx-large; padding-left: 5px"
|
||||
/>
|
||||
</b-input>
|
||||
<b-input :label="$t('communitys.form.date_period')">
|
||||
<flat-pickr
|
||||
class="form-control"
|
||||
v-model="date"
|
||||
:config="config"
|
||||
style="font-size: 0.5em; padding-left: 5px"
|
||||
></flat-pickr>
|
||||
</b-input>
|
||||
</b-col>
|
||||
<b-col lg="9">
|
||||
<b-input :label="$t('communitys.form.hours_report')">
|
||||
<textarea
|
||||
class="form-control"
|
||||
rows="5"
|
||||
@focus="textFocus"
|
||||
style="font-size: x-large; padding-left: 20px"
|
||||
></textarea>
|
||||
</b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<div ref="mydiv"></div>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col md="6">
|
||||
<b-button @click.prevent="newWorkForm" variant="warning">
|
||||
+ {{ $t('communitys.form.more_hours') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
<b-col md="6" class="text-right">
|
||||
<b-button variant="success" @click.prevent="submitForm2">
|
||||
{{ $t('communitys.form.submit') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-tab>
|
||||
|
||||
<b-tab :title="names.lastMonth"></b-tab>
|
||||
|
||||
<b-tab :title="names.beforLastMonth"></b-tab>
|
||||
</b-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'GDDAddWork2',
|
||||
data() {
|
||||
return {
|
||||
date: null,
|
||||
config: {
|
||||
altInput: false,
|
||||
dateFormat: 'd-m-Y',
|
||||
minDate: this.$moment().startOf('month').format('DD.MM.YYYY'),
|
||||
maxDate: this.$moment().format('DD.MM.YYYY'),
|
||||
mode: 'range',
|
||||
},
|
||||
index: 0,
|
||||
form: [],
|
||||
stundenSumme: 0,
|
||||
messages: [],
|
||||
submitted: false,
|
||||
days: {
|
||||
thisMonth: this.$moment().month(this.$moment().month()).daysInMonth(),
|
||||
lastMonth: this.$moment()
|
||||
.month(this.$moment().month() - 1)
|
||||
.daysInMonth(),
|
||||
beforLastMonth: this.$moment()
|
||||
.month(this.$moment().month() - 2)
|
||||
.daysInMonth(),
|
||||
},
|
||||
names: {
|
||||
thisMonth: this.$moment().month(this.$moment().month()).format('MMMM'),
|
||||
lastMonth: this.$moment()
|
||||
.month(this.$moment().month() - 1)
|
||||
.format('MMMM'),
|
||||
beforLastMonth: this.$moment()
|
||||
.month(this.$moment().month() - 2)
|
||||
.format('MMMM'),
|
||||
},
|
||||
formular: null,
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
watch: {
|
||||
$form: function () {
|
||||
this.stunden(this.form)
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
stunden(hour, i, mon) {
|
||||
let n = 0
|
||||
this.stundenSumme = 0
|
||||
for (n; n < this.form.length; n++) {
|
||||
if (this.form[n] > 0) {
|
||||
this.stundenSumme += parseInt(this.form[n])
|
||||
}
|
||||
}
|
||||
this.messages.push({
|
||||
id: this.index,
|
||||
MonthsNumber: mon,
|
||||
DaysNumber: i,
|
||||
HoursNumber: hour,
|
||||
DestinationText: '',
|
||||
TextDecoded: '',
|
||||
})
|
||||
this.index++
|
||||
},
|
||||
addNewMessage: function () {
|
||||
this.messages.push({
|
||||
DaysNumber: '',
|
||||
TextDecoded: '',
|
||||
})
|
||||
},
|
||||
deleteNewMessage: function (event) {
|
||||
this.form.splice(event, null)
|
||||
this.messages.splice(this.index, 1)
|
||||
this.index--
|
||||
},
|
||||
submitForm: function (e) {
|
||||
// console.log('submitForm')
|
||||
this.messages = [{ DaysNumber: '', TextDecoded: '' }]
|
||||
this.submitted = true
|
||||
},
|
||||
textFocus() {
|
||||
// console.log('textFocus TODO')
|
||||
},
|
||||
newWorkForm() {
|
||||
this.formular = `
|
||||
<b-col lg="3">
|
||||
<b-input label="Stunden">
|
||||
<b-form-input
|
||||
type="number"
|
||||
size="lg"
|
||||
placeholder="0"
|
||||
style="font-size: xx-large; padding-left: 20px"
|
||||
/>
|
||||
</b-input>
|
||||
<b-input label="Datum / Zeitraum">
|
||||
<flat-pickr
|
||||
class="form-control"
|
||||
v-model="date"
|
||||
:config="config"
|
||||
style="font-size: xx-large; padding-left: 20px"
|
||||
></flat-pickr>
|
||||
</b-input>
|
||||
</b-col>
|
||||
<b-col lg="9">
|
||||
<b-input label="Arbeitsreport">
|
||||
<textarea
|
||||
class="form-control"
|
||||
rows="5"
|
||||
@focus="textFocus"
|
||||
style="font-size: x-large; padding-left: 20px"
|
||||
></textarea>
|
||||
</b-input>
|
||||
</b-col>
|
||||
`
|
||||
|
||||
// console.log('newWorkForm TODO')
|
||||
const myElement = this.$refs.mydiv
|
||||
myElement.append(this.formular)
|
||||
this.$compile(myElement)
|
||||
this.formular = null
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -84,13 +84,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<pagination-buttons
|
||||
v-if="showPagination && transactionCount > pageSize"
|
||||
:has-next="hasNext"
|
||||
:has-previous="hasPrevious"
|
||||
:total-pages="totalPages"
|
||||
:current-page="currentPage"
|
||||
@show-next="showNext"
|
||||
@show-previous="showPrevious"
|
||||
v-if="showPagination"
|
||||
v-model="currentPage"
|
||||
:per-page="pageSize"
|
||||
:total-rows="transactionCount"
|
||||
></pagination-buttons>
|
||||
<div v-if="transactions.length === 0" class="mt-4 text-center">
|
||||
<span>{{ $t('transaction.nullTransactions') }}</span>
|
||||
@ -128,29 +125,13 @@ export default {
|
||||
transactionCount: { type: Number, default: 0 },
|
||||
showPagination: { type: Boolean, default: false },
|
||||
},
|
||||
watch: {
|
||||
timestamp: {
|
||||
immediate: true,
|
||||
handler: 'updateTransactions',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasNext() {
|
||||
return this.currentPage * this.pageSize < this.transactionCount
|
||||
},
|
||||
hasPrevious() {
|
||||
return this.currentPage > 1
|
||||
},
|
||||
totalPages() {
|
||||
return Math.ceil(this.transactionCount / this.pageSize)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateTransactions() {
|
||||
this.$emit('update-transactions', {
|
||||
firstPage: this.currentPage,
|
||||
items: this.pageSize,
|
||||
})
|
||||
window.scrollTo(0, 0)
|
||||
},
|
||||
getProperties(givenType) {
|
||||
const type = iconsByType[givenType]
|
||||
@ -165,15 +146,14 @@ export default {
|
||||
throwError(msg) {
|
||||
throw new Error(msg)
|
||||
},
|
||||
showNext() {
|
||||
this.currentPage++
|
||||
},
|
||||
watch: {
|
||||
currentPage() {
|
||||
this.updateTransactions()
|
||||
window.scrollTo(0, 0)
|
||||
},
|
||||
showPrevious() {
|
||||
this.currentPage--
|
||||
this.updateTransactions()
|
||||
window.scrollTo(0, 0)
|
||||
timestamp: {
|
||||
immediate: true,
|
||||
handler: 'updateTransactions',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-list-group>
|
||||
<b-list-group-item v-for="item in items" :key="item.id">
|
||||
<div class="d-flex w-100 justify-content-between" @click="toogle(item)">
|
||||
<b-icon
|
||||
v-if="item.status == 'submitted'"
|
||||
icon="clock-history"
|
||||
class="m-1"
|
||||
font-scale="2"
|
||||
style="color: orange"
|
||||
></b-icon>
|
||||
<b-icon v-else icon="check2-all" class="m-1" font-scale="2" style="color: green"></b-icon>
|
||||
<h2 class="text-muted">
|
||||
<small>{{ item.datel }}</small>
|
||||
- {{ item.text }}
|
||||
</h2>
|
||||
</div>
|
||||
</b-list-group-item>
|
||||
</b-list-group>
|
||||
<hr />
|
||||
<b-icon icon="clock-history" class="m-1" font-scale="2" style="color: orange"></b-icon>
|
||||
Wartet auf Bestätigung |
|
||||
<b-icon icon="check2-all" class="m-1" font-scale="2" style="color: green"></b-icon>
|
||||
bestätigt
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'GddWorkTable',
|
||||
data() {
|
||||
return {
|
||||
form: [],
|
||||
items: [
|
||||
{
|
||||
id: 1,
|
||||
text: 'Zwei Säcke Plastikmüll im Wald gesammelt',
|
||||
datel: '12.12.2020 14:04',
|
||||
status: 'submitted',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
text: 'Frau Schmidt bei der Gartenarbeit geholfen.',
|
||||
datel: '22.06.2020 22:23',
|
||||
status: 'submitted',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
text: 'Ein online Kurs für nachhaltiges Mülltrennen erstellt',
|
||||
datel: '15.04.2020 12:55',
|
||||
status: 'confirmed',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
text: 'Gradido bei meinen Freunden vorgestellt',
|
||||
datel: '10.03.2020 18:20',
|
||||
status: 'confirmed',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
rowClass(item, type) {
|
||||
if (!item || type !== 'row') return
|
||||
if (item.status === 'received') return 'table-success'
|
||||
if (item.status === 'sent') return 'table-warning'
|
||||
if (item.status === 'earned') return 'table-primary'
|
||||
},
|
||||
toogle(item) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const temp =
|
||||
'<b-collapse visible v-bind:id="item.id">xxx <small class="text-muted">porta</small></b-collapse>'
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.el-table .cell {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
</style>
|
||||
@ -46,6 +46,9 @@ const apolloMock = jest.fn().mockResolvedValue({
|
||||
})
|
||||
|
||||
const toastErrorMock = jest.fn()
|
||||
const windowScrollToMock = jest.fn()
|
||||
|
||||
window.scrollTo = windowScrollToMock
|
||||
|
||||
describe('GdtTransactionList', () => {
|
||||
let wrapper
|
||||
@ -90,6 +93,10 @@ describe('GdtTransactionList', () => {
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('scrolls to (0, 0) after API call', () => {
|
||||
expect(windowScrollToMock).toBeCalledWith(0, 0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('server returns error', () => {
|
||||
@ -105,5 +112,21 @@ describe('GdtTransactionList', () => {
|
||||
expect(toastErrorMock).toBeCalledWith('Ouch!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('change of currentPage', () => {
|
||||
it('calls the API after currentPage changes', async () => {
|
||||
jest.clearAllMocks()
|
||||
wrapper.setData({ currentPage: 2 })
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(apolloMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
currentPage: 2,
|
||||
pageSize: 25,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -28,13 +28,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<pagination-buttons
|
||||
v-if="transactionGdtCount > pageSize"
|
||||
:has-next="hasNext"
|
||||
:has-previous="hasPrevious"
|
||||
:total-pages="totalPages"
|
||||
:current-page="currentPage"
|
||||
@show-next="showNext"
|
||||
@show-previous="showPrevious"
|
||||
v-model="currentPage"
|
||||
:per-page="pageSize"
|
||||
:total-rows="transactionGdtCount"
|
||||
></pagination-buttons>
|
||||
</div>
|
||||
</template>
|
||||
@ -52,23 +48,12 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
transactionsGdt: { default: () => [] },
|
||||
transactionsGdt: [],
|
||||
transactionGdtCount: { type: Number, default: 0 },
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasNext() {
|
||||
return this.currentPage * this.pageSize < this.transactionGdtCount
|
||||
},
|
||||
hasPrevious() {
|
||||
return this.currentPage > 1
|
||||
},
|
||||
totalPages() {
|
||||
return Math.ceil(this.transactionGdtCount / this.pageSize)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async updateGdt() {
|
||||
this.$apollo
|
||||
@ -85,25 +70,21 @@ export default {
|
||||
} = result
|
||||
this.transactionsGdt = listGDTEntries.gdtEntries
|
||||
this.transactionGdtCount = listGDTEntries.count
|
||||
window.scrollTo(0, 0)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
})
|
||||
},
|
||||
showNext() {
|
||||
this.currentPage++
|
||||
this.updateGdt()
|
||||
window.scrollTo(0, 0)
|
||||
},
|
||||
showPrevious() {
|
||||
this.currentPage--
|
||||
this.updateGdt()
|
||||
window.scrollTo(0, 0)
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.updateGdt()
|
||||
},
|
||||
watch: {
|
||||
currentPage() {
|
||||
this.updateGdt()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
105
frontend/src/views/Pages/CheckEmail.spec.js
Normal file
105
frontend/src/views/Pages/CheckEmail.spec.js
Normal file
@ -0,0 +1,105 @@
|
||||
import { mount, RouterLinkStub } from '@vue/test-utils'
|
||||
import CheckEmail from './CheckEmail'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloQueryMock = jest.fn().mockRejectedValue({ message: 'error' })
|
||||
|
||||
const toasterMock = jest.fn()
|
||||
const routerPushMock = jest.fn()
|
||||
|
||||
describe('CheckEmail', () => {
|
||||
let wrapper
|
||||
|
||||
const mocks = {
|
||||
$i18n: {
|
||||
locale: 'en',
|
||||
},
|
||||
$t: jest.fn((t) => t),
|
||||
$route: {
|
||||
params: {
|
||||
optin: '123',
|
||||
},
|
||||
},
|
||||
$toasted: {
|
||||
error: toasterMock,
|
||||
},
|
||||
$router: {
|
||||
push: routerPushMock,
|
||||
},
|
||||
$loading: {
|
||||
show: jest.fn(() => {
|
||||
return { hide: jest.fn() }
|
||||
}),
|
||||
},
|
||||
$apollo: {
|
||||
query: apolloQueryMock,
|
||||
},
|
||||
}
|
||||
|
||||
const stubs = {
|
||||
RouterLink: RouterLinkStub,
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(CheckEmail, { localVue, mocks, stubs })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('calls the checkEmail when created', async () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({ variables: { optin: '123' } }),
|
||||
)
|
||||
})
|
||||
|
||||
describe('No valid optin', () => {
|
||||
it('toasts an error when no valid optin is given', () => {
|
||||
expect(toasterMock).toHaveBeenCalledWith('error')
|
||||
})
|
||||
|
||||
it('has a message suggesting to contact the support', () => {
|
||||
expect(wrapper.find('div.header').text()).toContain('checkEmail.title')
|
||||
expect(wrapper.find('div.header').text()).toContain('checkEmail.errorText')
|
||||
})
|
||||
})
|
||||
|
||||
describe('is authenticated', () => {
|
||||
beforeEach(() => {
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
data: {
|
||||
checkEmail: {
|
||||
sessionId: 1,
|
||||
email: 'user@example.org',
|
||||
language: 'de',
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it.skip('Has sessionId from API call', async () => {
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.vm.sessionId).toBe(1)
|
||||
})
|
||||
|
||||
describe('Register header', () => {
|
||||
it('has a welcome message', async () => {
|
||||
expect(wrapper.find('div.header').text()).toContain('checkEmail.title')
|
||||
})
|
||||
})
|
||||
|
||||
describe('links', () => {
|
||||
it('has a link "Back"', async () => {
|
||||
expect(wrapper.findAllComponents(RouterLinkStub).at(0).text()).toEqual('back')
|
||||
})
|
||||
|
||||
it('links to /login when clicking "Back"', async () => {
|
||||
expect(wrapper.findAllComponents(RouterLinkStub).at(0).props().to).toBe('/Login')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
72
frontend/src/views/Pages/CheckEmail.vue
Normal file
72
frontend/src/views/Pages/CheckEmail.vue
Normal file
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="checkemail-form">
|
||||
<b-container>
|
||||
<div class="header p-4" ref="header">
|
||||
<div class="header-body text-center mb-7">
|
||||
<b-row class="justify-content-center">
|
||||
<b-col xl="5" lg="6" md="8" class="px-2">
|
||||
<h1>{{ $t('checkEmail.title') }}</h1>
|
||||
<div class="pb-4" v-if="!pending">
|
||||
<span v-if="!authenticated">
|
||||
{{ $t('checkEmail.errorText') }}
|
||||
</span>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</div>
|
||||
</b-container>
|
||||
<b-container class="mt--8 p-1">
|
||||
<b-row>
|
||||
<b-col class="text-center py-lg-4">
|
||||
<router-link to="/Login" class="mt-3">{{ $t('back') }}</router-link>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { checkEmailQuery } from '../../graphql/queries'
|
||||
|
||||
export default {
|
||||
name: 'CheckEmail',
|
||||
data() {
|
||||
return {
|
||||
authenticated: false,
|
||||
sessionId: null,
|
||||
email: null,
|
||||
pending: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async authenticate() {
|
||||
const loader = this.$loading.show({
|
||||
container: this.$refs.header,
|
||||
})
|
||||
const optin = this.$route.params.optin
|
||||
this.$apollo
|
||||
.query({
|
||||
query: checkEmailQuery,
|
||||
variables: {
|
||||
optin: optin,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.authenticated = true
|
||||
this.sessionId = result.data.checkEmail.sessionId
|
||||
this.email = result.data.checkEmail.email
|
||||
this.$router.push('/thx/checkEmail')
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
})
|
||||
loader.hide()
|
||||
this.pending = false
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.authenticate()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
@ -22,6 +22,11 @@ describe('Register', () => {
|
||||
$apollo: {
|
||||
query: resgisterUserQueryMock,
|
||||
},
|
||||
$store: {
|
||||
state: {
|
||||
language: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const stubs = {
|
||||
@ -38,7 +43,7 @@ describe('Register', () => {
|
||||
})
|
||||
|
||||
it('renders the Register form', () => {
|
||||
expect(wrapper.find('div.register-form').exists()).toBeTruthy()
|
||||
expect(wrapper.find('div#registerform').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('Register header', () => {
|
||||
@ -81,11 +86,11 @@ describe('Register', () => {
|
||||
expect(wrapper.find('input[name="form.passwordRepeat"]').exists()).toBeTruthy()
|
||||
})
|
||||
it('has Language selected field', () => {
|
||||
expect(wrapper.find('#selectedLanguage').exists()).toBeTruthy()
|
||||
expect(wrapper.find('.selectedLanguage').exists()).toBeTruthy()
|
||||
})
|
||||
it('selected Language value de', async () => {
|
||||
wrapper.find('#selectedLanguage').findAll('option').at(1).setSelected()
|
||||
expect(wrapper.find('#selectedLanguage').element.value).toBe('de')
|
||||
wrapper.find('.selectedLanguage').findAll('option').at(1).setSelected()
|
||||
expect(wrapper.find('.selectedLanguage').element.value).toBe('de')
|
||||
})
|
||||
|
||||
it('has 1 checkbox input fields', () => {
|
||||
@ -128,14 +133,14 @@ describe('Register', () => {
|
||||
wrapper.find('#Email-input-field').setValue('max.mustermann@gradido.net')
|
||||
wrapper.find('input[name="form.password"]').setValue('Aa123456')
|
||||
wrapper.find('input[name="form.passwordRepeat"]').setValue('Aa123456')
|
||||
wrapper.find('#selectedLanguage').findAll('option').at(1).setSelected()
|
||||
wrapper.find('.language-switch-select').findAll('option').at(1).setSelected()
|
||||
wrapper.find('input[name="site.signup.agree"]').setChecked(true)
|
||||
})
|
||||
|
||||
it('reset selected value language', async () => {
|
||||
await wrapper.find('button.ml-2').trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.find('#selectedLanguage').element.value).toBe('')
|
||||
expect(wrapper.find('.language-switch-select').element.value).toBe(undefined)
|
||||
})
|
||||
|
||||
it('resets the firstName field after clicking the reset button', async () => {
|
||||
@ -182,7 +187,7 @@ describe('Register', () => {
|
||||
wrapper.find('#Email-input-field').setValue('max.mustermann@gradido.net')
|
||||
wrapper.find('input[name="form.password"]').setValue('Aa123456')
|
||||
wrapper.find('input[name="form.passwordRepeat"]').setValue('Aa123456')
|
||||
wrapper.find('#selectedLanguage').findAll('option').at(1).setSelected()
|
||||
wrapper.find('.language-switch-select').findAll('option').at(1).setSelected()
|
||||
})
|
||||
|
||||
describe('server sends back error', () => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="register-form">
|
||||
<div id="registerform">
|
||||
<!-- Header -->
|
||||
<div class="header p-4">
|
||||
<b-container class="container">
|
||||
@ -87,12 +87,7 @@
|
||||
<b-row>
|
||||
<b-col cols="12">
|
||||
{{ $t('language') }}
|
||||
<b-form-select
|
||||
id="selectedLanguage"
|
||||
v-model="selected"
|
||||
:options="options"
|
||||
class="mb-3"
|
||||
></b-form-select>
|
||||
<language-switch-select @update-language="updateLanguage" />
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
@ -145,10 +140,11 @@
|
||||
<script>
|
||||
import InputEmail from '../../components/Inputs/InputEmail.vue'
|
||||
import InputPasswordConfirmation from '../../components/Inputs/InputPasswordConfirmation.vue'
|
||||
import LanguageSwitchSelect from '../../components/LanguageSwitchSelect.vue'
|
||||
import { resgisterUserQuery } from '../../graphql/queries'
|
||||
|
||||
export default {
|
||||
components: { InputPasswordConfirmation, InputEmail },
|
||||
components: { InputPasswordConfirmation, InputEmail, LanguageSwitchSelect },
|
||||
name: 'register',
|
||||
data() {
|
||||
return {
|
||||
@ -162,12 +158,7 @@ export default {
|
||||
passwordRepeat: '',
|
||||
},
|
||||
},
|
||||
selected: null,
|
||||
options: [
|
||||
{ value: null, text: this.$t('select_language') },
|
||||
{ value: 'de', text: this.$t('languages.de') },
|
||||
{ value: 'en', text: this.$t('languages.en') },
|
||||
],
|
||||
language: '',
|
||||
submitted: false,
|
||||
showError: false,
|
||||
messageError: '',
|
||||
@ -175,6 +166,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateLanguage(e) {
|
||||
this.language = e
|
||||
},
|
||||
getValidationState({ dirty, validated, valid = null }) {
|
||||
return dirty || validated ? valid : null
|
||||
},
|
||||
@ -189,7 +183,7 @@ export default {
|
||||
},
|
||||
agree: false,
|
||||
}
|
||||
this.selected = null
|
||||
this.language = ''
|
||||
this.$nextTick(() => {
|
||||
this.$refs.observer.reset()
|
||||
})
|
||||
@ -203,7 +197,7 @@ export default {
|
||||
firstName: this.form.firstname,
|
||||
lastName: this.form.lastname,
|
||||
password: this.form.password.password,
|
||||
language: this.selected,
|
||||
language: this.language,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
@ -212,7 +206,7 @@ export default {
|
||||
this.form.lastname = ''
|
||||
this.form.password.password = ''
|
||||
this.form.password.passwordRepeat = ''
|
||||
this.selected = null
|
||||
this.language = ''
|
||||
this.$router.push('/thx/register')
|
||||
})
|
||||
.catch((error) => {
|
||||
@ -228,7 +222,7 @@ export default {
|
||||
this.form.lastname = ''
|
||||
this.form.password.password = ''
|
||||
this.form.password.passwordRepeat = ''
|
||||
this.selected = null
|
||||
this.language = ''
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
@ -244,7 +238,7 @@ export default {
|
||||
return this.form.email !== ''
|
||||
},
|
||||
languageFilled() {
|
||||
return this.selected !== null
|
||||
return this.language !== null && this.language !== ''
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<b-card class="bg-transparent">
|
||||
<div class="w-100 text-center">
|
||||
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
|
||||
<vue-qrcode
|
||||
v-if="$store.state.email"
|
||||
:value="$store.state.email"
|
||||
type="image/png"
|
||||
></vue-qrcode>
|
||||
</div>
|
||||
|
||||
<div class="card-profile-stats d-flex justify-content-center mt-md-5">
|
||||
|
||||
@ -1,81 +1,75 @@
|
||||
<template>
|
||||
<div class="userdata_form" fluid="sm">
|
||||
<div
|
||||
id="userdata_form"
|
||||
class="bg-transparent pt-3 pb-3"
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
<b-container>
|
||||
<b-row class="text-right">
|
||||
<b-col class="mb-3">
|
||||
<a @click="showUserData ? (showUserData = !showUserData) : cancelEdit()">
|
||||
<span class="pointer mr-3">{{ $t('form.change-name') }}</span>
|
||||
<b-card id="userdata_form" class="bg-transparent" style="background-color: #ebebeba3 !important">
|
||||
<div>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a @click="showUserData ? (showUserData = !showUserData) : cancelEdit()">
|
||||
<span class="pointer mr-3">{{ $t('form.change-name') }}</span>
|
||||
<b-icon v-if="showUserData" class="pointer ml-3" icon="pencil"></b-icon>
|
||||
<b-icon v-else icon="x-circle" class="pointer ml-3" variant="danger"></b-icon>
|
||||
</a>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
<b-icon v-if="showUserData" class="pointer" icon="pencil">
|
||||
{{ $t('form.change') }}
|
||||
</b-icon>
|
||||
|
||||
<b-icon v-else class="pointer" icon="x-circle" variant="danger"></b-icon>
|
||||
</a>
|
||||
<div>
|
||||
<b-form @keyup.prevent="loadSubmitButton">
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-12">
|
||||
<small>
|
||||
<b>{{ $t('form.firstname') }}</b>
|
||||
</small>
|
||||
</b-col>
|
||||
<b-col v-if="showUserData" class="col-12">
|
||||
{{ form.firstName }}
|
||||
</b-col>
|
||||
<b-col v-else class="col-12">
|
||||
<b-input type="text" v-model="form.firstName"></b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-12">
|
||||
<small>
|
||||
<b>{{ $t('form.lastname') }}</b>
|
||||
</small>
|
||||
</b-col>
|
||||
<b-col v-if="showUserData" class="col-12">
|
||||
{{ form.lastName }}
|
||||
</b-col>
|
||||
<b-col v-else class="col-12">
|
||||
<b-input type="text" v-model="form.lastName"></b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="mb-3" v-show="false">
|
||||
<b-col class="col-12">
|
||||
<small>{{ $t('form.description') }}</small>
|
||||
</b-col>
|
||||
<b-col v-if="showUserData" class="col-12">
|
||||
{{ form.description }}
|
||||
</b-col>
|
||||
<b-col v-else class="col-12">
|
||||
<b-textarea rows="3" max-rows="6" v-model="form.description"></b-textarea>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
|
||||
<b-container>
|
||||
<b-form @keyup.prevent="loadSubmitButton">
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-12 col-lg-3 col-md-12 col-sm-12 text-md-left text-lg-right">
|
||||
<small>{{ $t('form.firstname') }}</small>
|
||||
</b-col>
|
||||
<b-col v-if="showUserData" class="col-sm-10 col-md-9">
|
||||
{{ form.firstName }}
|
||||
</b-col>
|
||||
<b-col v-else class="col-md-9 col-sm-10">
|
||||
<b-input type="text" v-model="form.firstName"></b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-12 col-lg-3 col-md-12 col-sm-12 text-md-left text-lg-right">
|
||||
<small>{{ $t('form.lastname') }}</small>
|
||||
</b-col>
|
||||
<b-col v-if="showUserData" class="col-sm-10 col-md-9">
|
||||
{{ form.lastName }}
|
||||
</b-col>
|
||||
<b-col v-else class="col-md-9 col-sm-10">
|
||||
<b-input type="text" v-model="form.lastName"></b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="mb-3" v-show="false">
|
||||
<b-col class="col-12 col-lg-3 col-md-10 col-sm-10 text-md-left text-lg-right">
|
||||
<small>{{ $t('form.description') }}</small>
|
||||
</b-col>
|
||||
<b-col v-if="showUserData" class="col-sm-10 col-md-9">
|
||||
{{ form.description }}
|
||||
</b-col>
|
||||
<b-col v-else class="col-sm-10 col-md-9">
|
||||
<b-textarea rows="3" max-rows="6" v-model="form.description"></b-textarea>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<b-row class="text-right" v-if="!showUserData">
|
||||
<b-col>
|
||||
<div class="text-right" ref="submitButton">
|
||||
<b-button
|
||||
:variant="loading ? 'default' : 'success'"
|
||||
@click="onSubmit"
|
||||
type="submit"
|
||||
class="mt-4"
|
||||
:disabled="loading"
|
||||
>
|
||||
{{ $t('form.save') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
</b-container>
|
||||
<b-row class="text-right" v-if="!showUserData">
|
||||
<b-col>
|
||||
<div class="text-right" ref="submitButton">
|
||||
<b-button
|
||||
:variant="loading ? 'default' : 'success'"
|
||||
@click="onSubmit"
|
||||
type="submit"
|
||||
class="mt-4"
|
||||
:disabled="loading"
|
||||
>
|
||||
{{ $t('form.save') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
</div>
|
||||
</div>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import { updateUserInfos } from '../../../graphql/queries'
|
||||
|
||||
@ -1,48 +1,43 @@
|
||||
<template>
|
||||
<div
|
||||
id="change_pwd"
|
||||
class="bg-transparent pt-3 pb-3"
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
<b-container>
|
||||
<div>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a @click="!editPassword ? (editPassword = !editPassword) : cancelEdit()">
|
||||
<span class="pointer mr-3">{{ $t('form.change-password') }}</span>
|
||||
<b-icon v-if="!editPassword" class="pointer ml-3" icon="pencil" />
|
||||
<b-icon v-else icon="x-circle" class="pointer ml-3" variant="danger"></b-icon>
|
||||
</a>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
<div v-if="editPassword">
|
||||
<validation-observer ref="observer" v-slot="{ handleSubmit }">
|
||||
<b-form @submit.stop.prevent="handleSubmit(onSubmit)">
|
||||
<b-row class="mb-2">
|
||||
<b-col>
|
||||
<input-password
|
||||
:label="$t('form.password_old')"
|
||||
:placeholder="$t('form.password_old')"
|
||||
v-model="form.password"
|
||||
></input-password>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<input-password-confirmation v-model="form.newPassword" :register="register" />
|
||||
<b-row class="text-right">
|
||||
<b-col>
|
||||
<div class="text-right">
|
||||
<b-button type="submit" variant="primary" class="mt-4">
|
||||
{{ $t('form.save') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
</validation-observer>
|
||||
</div>
|
||||
</b-container>
|
||||
</div>
|
||||
<b-card id="change_pwd" class="bg-transparent" style="background-color: #ebebeba3 !important">
|
||||
<div>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a @click="showPassword ? (showPassword = !showPassword) : cancelEdit()">
|
||||
<span class="pointer mr-3">{{ $t('form.change-password') }}</span>
|
||||
<b-icon v-if="showPassword" class="pointer ml-3" icon="pencil"></b-icon>
|
||||
<b-icon v-else icon="x-circle" class="pointer ml-3" variant="danger"></b-icon>
|
||||
</a>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
<div v-if="!showPassword">
|
||||
<validation-observer ref="observer" v-slot="{ handleSubmit }">
|
||||
<b-form @submit.stop.prevent="handleSubmit(onSubmit)">
|
||||
<b-row class="mb-2">
|
||||
<b-col>
|
||||
<input-password
|
||||
:label="$t('form.password_old')"
|
||||
:placeholder="$t('form.password_old')"
|
||||
v-model="form.password"
|
||||
></input-password>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<input-password-confirmation v-model="form.newPassword" :register="register" />
|
||||
<b-row class="text-right">
|
||||
<b-col>
|
||||
<div class="text-right">
|
||||
<b-button type="submit" variant="primary" class="mt-4">
|
||||
{{ $t('form.save') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
</validation-observer>
|
||||
</div>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import InputPassword from '../../../components/Inputs/InputPassword'
|
||||
@ -57,7 +52,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editPassword: false,
|
||||
showPassword: true,
|
||||
email: null,
|
||||
form: {
|
||||
password: '',
|
||||
@ -71,7 +66,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
cancelEdit() {
|
||||
this.editPassword = false
|
||||
this.showPassword = true
|
||||
this.form.password = ''
|
||||
this.form.passwordNew = ''
|
||||
this.form.passwordNewRepeat = ''
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import UserCardLanguage from './UserCard_Language'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const mockAPIcall = jest.fn()
|
||||
|
||||
const toastErrorMock = jest.fn()
|
||||
const toastSuccessMock = jest.fn()
|
||||
const storeCommitMock = jest.fn()
|
||||
|
||||
describe('UserCard_Language', () => {
|
||||
let wrapper
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$store: {
|
||||
state: {
|
||||
language: 'de',
|
||||
},
|
||||
commit: storeCommitMock,
|
||||
},
|
||||
$toasted: {
|
||||
success: toastSuccessMock,
|
||||
error: toastErrorMock,
|
||||
},
|
||||
$apollo: {
|
||||
query: mockAPIcall,
|
||||
},
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(UserCardLanguage, { localVue, mocks })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders the component', () => {
|
||||
expect(wrapper.find('div#formuserlanguage').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has an edit icon', () => {
|
||||
expect(wrapper.find('svg.bi-pencil').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
108
frontend/src/views/Pages/UserProfile/UserCard_Language.vue
Normal file
108
frontend/src/views/Pages/UserProfile/UserCard_Language.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<b-card
|
||||
id="formuserlanguage"
|
||||
class="bg-transparent"
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
<div>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a @click="showLanguage ? (showLanguage = !showLanguage) : cancelEdit()">
|
||||
<span class="pointer mr-3">{{ $t('form.changeLanguage') }}</span>
|
||||
<b-icon v-if="showLanguage" class="pointer ml-3" icon="pencil"></b-icon>
|
||||
<b-icon v-else icon="x-circle" class="pointer ml-3" variant="danger"></b-icon>
|
||||
</a>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
<div v-if="showLanguage">
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-12">
|
||||
<small>
|
||||
<b>{{ $t('language') }}</b>
|
||||
</small>
|
||||
</b-col>
|
||||
<b-col class="col-12">{{ $store.state.language }}</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div>
|
||||
<b-form @submit.stop.prevent="handleSubmit(onSubmit)">
|
||||
<b-row class="mb-2">
|
||||
<b-col class="col-12">
|
||||
<small>
|
||||
<b>{{ $t('language') }}</b>
|
||||
</small>
|
||||
</b-col>
|
||||
<b-col class="col-12">
|
||||
<language-switch-select @update-language="updateLanguage" :language="language" />
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<b-row class="text-right">
|
||||
<b-col>
|
||||
<div class="text-right" ref="submitButton">
|
||||
<b-button
|
||||
:variant="loading ? 'default' : 'success'"
|
||||
@click="onSubmit"
|
||||
type="submit"
|
||||
class="mt-4"
|
||||
:disabled="loading"
|
||||
>
|
||||
{{ $t('form.save') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
</div>
|
||||
</div>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import LanguageSwitchSelect from '../../../components/LanguageSwitchSelect.vue'
|
||||
import { updateUserInfos } from '../../../graphql/queries'
|
||||
|
||||
export default {
|
||||
name: 'FormUserLanguage',
|
||||
components: { LanguageSwitchSelect },
|
||||
data() {
|
||||
return {
|
||||
showLanguage: true,
|
||||
language: '',
|
||||
loading: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateLanguage(e) {
|
||||
this.language = e
|
||||
if (this.language !== this.$store.state.language) {
|
||||
this.loading = false
|
||||
} else {
|
||||
this.loading = true
|
||||
}
|
||||
},
|
||||
cancelEdit() {
|
||||
this.showLanguage = true
|
||||
},
|
||||
|
||||
async onSubmit() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: updateUserInfos,
|
||||
variables: {
|
||||
language: this.$store.state.language,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.cancelEdit()
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,98 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import UserCardNewsletter from './UserCard_Newsletter'
|
||||
import { unsubscribeNewsletter } from '../../../graphql/mutations'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const mockAPIcall = jest.fn()
|
||||
|
||||
const toastErrorMock = jest.fn()
|
||||
const toastSuccessMock = jest.fn()
|
||||
const storeCommitMock = jest.fn()
|
||||
const newsletterStateMock = jest.fn().mockReturnValue(true)
|
||||
|
||||
describe('UserCard_Newsletter', () => {
|
||||
let wrapper
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$store: {
|
||||
state: {
|
||||
language: 'de',
|
||||
email: 'peter@lustig.de',
|
||||
newsletterState: newsletterStateMock,
|
||||
},
|
||||
commit: storeCommitMock,
|
||||
},
|
||||
$toasted: {
|
||||
success: toastSuccessMock,
|
||||
error: toastErrorMock,
|
||||
},
|
||||
$apollo: {
|
||||
mutate: mockAPIcall,
|
||||
},
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(UserCardNewsletter, { localVue, mocks })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders the component', () => {
|
||||
expect(wrapper.find('div#formusernewsletter').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has an edit BFormCheckbox switch', () => {
|
||||
expect(wrapper.find('.Test-BFormCheckbox').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('unsubscribe with sucess', () => {
|
||||
beforeEach(() => {
|
||||
mockAPIcall.mockResolvedValue({
|
||||
data: {
|
||||
unsubscribeNewsletter: true,
|
||||
},
|
||||
})
|
||||
wrapper.find('input').trigger('change')
|
||||
})
|
||||
|
||||
it('calls the unsubscribe mutation', () => {
|
||||
expect(mockAPIcall).toBeCalledWith({
|
||||
mutation: unsubscribeNewsletter,
|
||||
variables: {
|
||||
email: 'peter@lustig.de',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('updates the store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('newsletterState', false)
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessMock).toBeCalledWith('setting.newsletterFalse')
|
||||
})
|
||||
})
|
||||
|
||||
describe('unsubscribe with server error', () => {
|
||||
beforeEach(() => {
|
||||
mockAPIcall.mockRejectedValue({
|
||||
message: 'Ouch',
|
||||
})
|
||||
wrapper.find('input').trigger('change')
|
||||
})
|
||||
|
||||
it('resets the newsletterState', () => {
|
||||
expect(wrapper.vm.newsletterState).toBeTruthy()
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorMock).toBeCalledWith('Ouch')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
64
frontend/src/views/Pages/UserProfile/UserCard_Newsletter.vue
Normal file
64
frontend/src/views/Pages/UserProfile/UserCard_Newsletter.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<b-card
|
||||
id="formusernewsletter"
|
||||
class="bg-transparent"
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
<div>
|
||||
<b-row class="mb-3">
|
||||
<b-col class="mb-2 col-12">
|
||||
<small>
|
||||
<b>{{ $t('setting.newsletter') }}</b>
|
||||
</small>
|
||||
</b-col>
|
||||
<b-col class="col-12">
|
||||
<b-form-checkbox
|
||||
class="Test-BFormCheckbox"
|
||||
v-model="newsletterState"
|
||||
name="check-button"
|
||||
switch
|
||||
@change="onSubmit"
|
||||
>
|
||||
{{ newsletterState ? $t('setting.newsletterTrue') : $t('setting.newsletterFalse') }}
|
||||
</b-form-checkbox>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import { subscribeNewsletter, unsubscribeNewsletter } from '../../../graphql/mutations'
|
||||
|
||||
export default {
|
||||
name: 'FormUserNewsletter',
|
||||
data() {
|
||||
return {
|
||||
newsletterState: this.$store.state.newsletterState,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onSubmit() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: this.newsletterState ? subscribeNewsletter : unsubscribeNewsletter,
|
||||
variables: {
|
||||
email: this.$store.state.email,
|
||||
language: this.newsletterState ? this.$store.state.language : undefined,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.$store.commit('newsletterState', this.newsletterState)
|
||||
this.$toasted.success(
|
||||
this.newsletterState
|
||||
? this.$t('setting.newsletterTrue')
|
||||
: this.$t('setting.newsletterFalse'),
|
||||
)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.newsletterState = this.$store.state.newsletterState
|
||||
this.$toasted.error(error.message)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -23,16 +23,16 @@ describe('UserProfileOverview', () => {
|
||||
expect(wrapper.findComponent({ name: 'UserCard' }).exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has a user data form', () => {
|
||||
it('has a user first and last name form', () => {
|
||||
expect(wrapper.findComponent({ name: 'FormUserData' }).exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
// it('has a user name form', () => {
|
||||
// expect(wrapper.findComponent({ name: 'FormUsername' }).exists()).toBeTruthy()
|
||||
// })
|
||||
|
||||
it('has a user password form', () => {
|
||||
it('has a user change password form', () => {
|
||||
expect(wrapper.findComponent({ name: 'FormUserPasswort' }).exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has a user change language form', () => {
|
||||
expect(wrapper.findComponent({ name: 'FormUserLanguage' }).exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,24 +1,29 @@
|
||||
<template>
|
||||
<div fluid="sm">
|
||||
<user-card :balance="balance" :transactionCount="transactionCount"></user-card>
|
||||
<p><form-user-data /></p>
|
||||
<!--<form-username />-->
|
||||
<form-user-data />
|
||||
<hr />
|
||||
<p><form-user-passwort /></p>
|
||||
<form-user-passwort />
|
||||
<hr />
|
||||
<form-user-language />
|
||||
<hr />
|
||||
<form-user-newsletter />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import UserCard from './UserProfile/UserCard.vue'
|
||||
import FormUserData from './UserProfile/UserCard_FormUserData.vue'
|
||||
// import FormUsername from './UserProfile/UserCard_FormUsername.vue'
|
||||
import FormUserPasswort from './UserProfile/UserCard_FormUserPasswort.vue'
|
||||
import FormUserLanguage from './UserProfile/UserCard_Language.vue'
|
||||
import FormUserNewsletter from './UserProfile/UserCard_Newsletter.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UserCard,
|
||||
FormUserData,
|
||||
// FormUsername,
|
||||
FormUserPasswort,
|
||||
FormUserLanguage,
|
||||
FormUserNewsletter,
|
||||
},
|
||||
props: {
|
||||
balance: { type: Number, default: 0 },
|
||||
|
||||
@ -3,6 +3,8 @@ import UserProfileTransactionList from './UserProfileTransactionList'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
window.scrollTo = jest.fn()
|
||||
|
||||
describe('UserProfileTransactionList', () => {
|
||||
let wrapper
|
||||
|
||||
|
||||
@ -31,6 +31,11 @@ const textFields = {
|
||||
button: 'site.login.signin',
|
||||
linkTo: '/overview',
|
||||
},
|
||||
checkEmail: {
|
||||
subtitle: 'site.thx.checkEmail',
|
||||
button: 'login',
|
||||
linkTo: '/login',
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@ -1796,6 +1796,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
|
||||
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
|
||||
|
||||
"@types/json-schema@^7.0.8":
|
||||
version "7.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
|
||||
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
|
||||
|
||||
"@types/json5@^0.0.29":
|
||||
version "0.0.29"
|
||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
@ -1806,6 +1811,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
||||
|
||||
"@types/minimist@^1.2.0":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c"
|
||||
integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==
|
||||
|
||||
"@types/node@*":
|
||||
version "14.14.31"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055"
|
||||
@ -2495,7 +2505,7 @@ ajv@^5.2.3, ajv@^5.3.0:
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
json-schema-traverse "^0.3.0"
|
||||
|
||||
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4:
|
||||
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
|
||||
version "6.12.6"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
|
||||
@ -2752,11 +2762,6 @@ array-equal@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
|
||||
integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
|
||||
|
||||
array-find-index@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
|
||||
integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
|
||||
|
||||
array-flatten@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
@ -2809,6 +2814,11 @@ array.prototype.flat@^1.2.3:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.18.0-next.1"
|
||||
|
||||
arrify@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
|
||||
|
||||
asn1.js@^5.2.0:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
|
||||
@ -3293,13 +3303,6 @@ bindings@^1.5.0:
|
||||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
block-stream@*:
|
||||
version "0.0.9"
|
||||
resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
|
||||
integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=
|
||||
dependencies:
|
||||
inherits "~2.0.0"
|
||||
|
||||
bluebird@^3.1.1, bluebird@^3.5.1, bluebird@^3.5.5:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||
@ -3711,18 +3714,14 @@ camel-case@3.0.x:
|
||||
no-case "^2.2.0"
|
||||
upper-case "^1.1.1"
|
||||
|
||||
camelcase-keys@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
|
||||
integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc=
|
||||
camelcase-keys@^6.2.2:
|
||||
version "6.2.2"
|
||||
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0"
|
||||
integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==
|
||||
dependencies:
|
||||
camelcase "^2.0.0"
|
||||
map-obj "^1.0.0"
|
||||
|
||||
camelcase@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
|
||||
integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
|
||||
camelcase "^5.3.1"
|
||||
map-obj "^4.0.0"
|
||||
quick-lru "^4.0.1"
|
||||
|
||||
camelcase@^4.1.0:
|
||||
version "4.1.0"
|
||||
@ -3876,6 +3875,11 @@ chownr@^1.0.1, chownr@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
||||
|
||||
chownr@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
|
||||
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
|
||||
|
||||
chrome-trace-event@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4"
|
||||
@ -4012,15 +4016,6 @@ cliui@^7.0.2:
|
||||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
clone-deep@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
|
||||
integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
|
||||
dependencies:
|
||||
is-plain-object "^2.0.4"
|
||||
kind-of "^6.0.2"
|
||||
shallow-clone "^3.0.0"
|
||||
|
||||
clone@2.x, clone@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
|
||||
@ -4376,14 +4371,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
cross-spawn@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
|
||||
integrity sha1-ElYDfsufDF9549bvE14wdwGEuYI=
|
||||
dependencies:
|
||||
lru-cache "^4.0.1"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
||||
@ -4404,7 +4391,7 @@ cross-spawn@^6.0.0:
|
||||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-spawn@^7.0.0, cross-spawn@^7.0.2:
|
||||
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
|
||||
@ -4629,13 +4616,6 @@ current-script-polyfill@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/current-script-polyfill/-/current-script-polyfill-1.0.0.tgz#f31cf7e4f3e218b0726e738ca92a02d3488ef615"
|
||||
integrity sha1-8xz35PPiGLBybnOMqSoC00iO9hU=
|
||||
|
||||
currently-unhandled@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
|
||||
integrity sha1-mI3zP+qxke95mmE2nddsF635V+o=
|
||||
dependencies:
|
||||
array-find-index "^1.0.1"
|
||||
|
||||
cyclist@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
||||
@ -4997,7 +4977,15 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
decamelize@^1.1.2, decamelize@^1.2.0:
|
||||
decamelize-keys@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
|
||||
integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=
|
||||
dependencies:
|
||||
decamelize "^1.1.0"
|
||||
map-obj "^1.0.0"
|
||||
|
||||
decamelize@^1.1.0, decamelize@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||
@ -5315,10 +5303,10 @@ dot-prop@^5.2.0:
|
||||
dependencies:
|
||||
is-obj "^2.0.0"
|
||||
|
||||
dotenv-defaults@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dotenv-defaults/-/dotenv-defaults-2.0.1.tgz#ea6f9632b3b5cc55e48b736760def5561f1cb7c0"
|
||||
integrity sha512-ugFCyBF7ILuwpmznduHPQZBMucHHJ8T4OBManTEVjemxCm2+nqifSuW2lD2SNKdiKSH1E324kZSdJ8M04b4I/A==
|
||||
dotenv-defaults@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz#6b3ec2e4319aafb70940abda72d3856770ee77ac"
|
||||
integrity sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==
|
||||
dependencies:
|
||||
dotenv "^8.2.0"
|
||||
|
||||
@ -5327,12 +5315,12 @@ dotenv-expand@^5.1.0:
|
||||
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
|
||||
integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
|
||||
|
||||
dotenv-webpack@^6.0.4:
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-6.0.4.tgz#4874045d408598e45a95519d3cc71017c91c9104"
|
||||
integrity sha512-WiTPNLanDNJ1O8AvgkBpsbarw78a4PMYG2EfJcQoxTHFWy+ji213HR+3f4PhWB1RBumiD9cbiuC3SNxJXbBp9g==
|
||||
dotenv-webpack@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-7.0.3.tgz#f50ec3c7083a69ec6076e110566720003b7b107b"
|
||||
integrity sha512-O0O9pOEwrk+n1zzR3T2uuXRlw64QxHSPeNN1GaiNBloQFNaCUL9V8jxSVz4jlXXFP/CIqK8YecWf8BAvsSgMjw==
|
||||
dependencies:
|
||||
dotenv-defaults "^2.0.1"
|
||||
dotenv-defaults "^2.0.2"
|
||||
|
||||
dotenv@^7.0.0:
|
||||
version "7.0.0"
|
||||
@ -5485,6 +5473,11 @@ entities@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
|
||||
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
||||
|
||||
env-paths@^2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
||||
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
|
||||
|
||||
errno@^0.1.3, errno@~0.1.7:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
|
||||
@ -6639,6 +6632,13 @@ fs-extra@^8.1.0:
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-minipass@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
|
||||
integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
|
||||
dependencies:
|
||||
minipass "^3.0.0"
|
||||
|
||||
fs-write-stream-atomic@^1.0.8:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
|
||||
@ -6667,16 +6667,6 @@ fsevents@^2.1.2, fsevents@~2.3.1:
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||
|
||||
fstream@^1.0.0, fstream@^1.0.12:
|
||||
version "1.0.12"
|
||||
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
|
||||
integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
inherits "~2.0.0"
|
||||
mkdirp ">=0.5 0"
|
||||
rimraf "2"
|
||||
|
||||
function-bind@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
@ -6916,6 +6906,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
|
||||
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
|
||||
|
||||
graceful-fs@^4.2.3:
|
||||
version "4.2.8"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
|
||||
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
|
||||
|
||||
graphql-tag@^2.4.2:
|
||||
version "2.12.5"
|
||||
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.5.tgz#5cff974a67b417747d05c8d9f5f3cb4495d0db8f"
|
||||
@ -6959,6 +6954,11 @@ har-validator@~5.1.3:
|
||||
ajv "^6.12.3"
|
||||
har-schema "^2.0.0"
|
||||
|
||||
hard-rejection@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
|
||||
integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==
|
||||
|
||||
harmony-reflect@^1.4.6:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.1.tgz#c108d4f2bb451efef7a37861fdbdae72c9bdefa9"
|
||||
@ -7102,6 +7102,13 @@ hosted-git-info@^2.1.4:
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
||||
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
|
||||
|
||||
hosted-git-info@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961"
|
||||
integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
hpack.js@^2.1.6:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
|
||||
@ -7381,17 +7388,10 @@ imurmurhash@^0.1.4:
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
|
||||
|
||||
in-publish@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.1.tgz#948b1a535c8030561cea522f73f78f4be357e00c"
|
||||
integrity sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==
|
||||
|
||||
indent-string@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
|
||||
integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=
|
||||
dependencies:
|
||||
repeating "^2.0.0"
|
||||
indent-string@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
|
||||
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
|
||||
|
||||
indexes-of@^1.0.1:
|
||||
version "1.0.1"
|
||||
@ -7411,7 +7411,7 @@ inflight@^1.0.4:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
@ -7591,6 +7591,13 @@ is-core-module@^2.2.0:
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-core-module@^2.5.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19"
|
||||
integrity sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-data-descriptor@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
|
||||
@ -7655,11 +7662,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
|
||||
|
||||
is-finite@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3"
|
||||
integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==
|
||||
|
||||
is-fullwidth-code-point@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
|
||||
@ -7742,7 +7744,7 @@ is-path-inside@^2.1.0:
|
||||
dependencies:
|
||||
path-is-inside "^1.0.2"
|
||||
|
||||
is-plain-obj@^1.0.0:
|
||||
is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
||||
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
|
||||
@ -7806,11 +7808,6 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||
|
||||
is-utf8@^0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
|
||||
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
|
||||
|
||||
is-valid-glob@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa"
|
||||
@ -8995,7 +8992,7 @@ kind-of@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
|
||||
integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
|
||||
|
||||
kind-of@^6.0.0, kind-of@^6.0.2:
|
||||
kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
|
||||
@ -9005,6 +9002,11 @@ kleur@^3.0.3:
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
||||
|
||||
klona@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
|
||||
integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==
|
||||
|
||||
launch-editor-middleware@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/launch-editor-middleware/-/launch-editor-middleware-2.2.1.tgz#e14b07e6c7154b0a4b86a0fd345784e45804c157"
|
||||
@ -9051,17 +9053,6 @@ lines-and-columns@^1.1.6:
|
||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
|
||||
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
|
||||
|
||||
load-json-file@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
|
||||
integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
parse-json "^2.2.0"
|
||||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
strip-bom "^2.0.0"
|
||||
|
||||
load-json-file@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
|
||||
@ -9105,7 +9096,7 @@ loader-utils@^0.2.16:
|
||||
json5 "^0.5.0"
|
||||
object-assign "^4.0.1"
|
||||
|
||||
loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0:
|
||||
loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
|
||||
integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
|
||||
@ -9240,14 +9231,6 @@ loose-envify@^1.0.0, loose-envify@^1.2.0:
|
||||
dependencies:
|
||||
js-tokens "^3.0.0 || ^4.0.0"
|
||||
|
||||
loud-rejection@^1.0.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
|
||||
integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=
|
||||
dependencies:
|
||||
currently-unhandled "^0.4.1"
|
||||
signal-exit "^3.0.0"
|
||||
|
||||
lower-case@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
|
||||
@ -9321,11 +9304,16 @@ map-cache@^0.2.2:
|
||||
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
|
||||
integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
|
||||
|
||||
map-obj@^1.0.0, map-obj@^1.0.1:
|
||||
map-obj@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
|
||||
integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=
|
||||
|
||||
map-obj@^4.0.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.1.tgz#e4ea399dbc979ae735c83c863dd31bdf364277b7"
|
||||
integrity sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==
|
||||
|
||||
map-visit@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
|
||||
@ -9373,21 +9361,23 @@ memory-fs@^0.5.0:
|
||||
errno "^0.1.3"
|
||||
readable-stream "^2.0.1"
|
||||
|
||||
meow@^3.7.0:
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
|
||||
integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=
|
||||
meow@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364"
|
||||
integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==
|
||||
dependencies:
|
||||
camelcase-keys "^2.0.0"
|
||||
decamelize "^1.1.2"
|
||||
loud-rejection "^1.0.0"
|
||||
map-obj "^1.0.1"
|
||||
minimist "^1.1.3"
|
||||
normalize-package-data "^2.3.4"
|
||||
object-assign "^4.0.1"
|
||||
read-pkg-up "^1.0.1"
|
||||
redent "^1.0.0"
|
||||
trim-newlines "^1.0.0"
|
||||
"@types/minimist" "^1.2.0"
|
||||
camelcase-keys "^6.2.2"
|
||||
decamelize "^1.2.0"
|
||||
decamelize-keys "^1.1.0"
|
||||
hard-rejection "^2.1.0"
|
||||
minimist-options "4.1.0"
|
||||
normalize-package-data "^3.0.0"
|
||||
read-pkg-up "^7.0.1"
|
||||
redent "^3.0.0"
|
||||
trim-newlines "^3.0.0"
|
||||
type-fest "^0.18.0"
|
||||
yargs-parser "^20.2.3"
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
@ -9490,6 +9480,11 @@ mimic-fn@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
min-indent@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
|
||||
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
|
||||
|
||||
mini-css-extract-plugin@^0.8.0:
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz#a875e169beb27c88af77dd962771c9eedc3da161"
|
||||
@ -9517,11 +9512,35 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist-options@4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
|
||||
integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==
|
||||
dependencies:
|
||||
arrify "^1.0.1"
|
||||
is-plain-obj "^1.1.0"
|
||||
kind-of "^6.0.3"
|
||||
|
||||
minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
minipass@^3.0.0:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.5.tgz#71f6251b0a33a49c01b3cf97ff77eda030dff732"
|
||||
integrity sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
minizlib@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
|
||||
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
|
||||
dependencies:
|
||||
minipass "^3.0.0"
|
||||
yallist "^4.0.0"
|
||||
|
||||
mississippi@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f"
|
||||
@ -9562,14 +9581,14 @@ mixin-deep@^1.2.0:
|
||||
for-in "^1.0.2"
|
||||
is-extendable "^1.0.1"
|
||||
|
||||
mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
|
||||
mkdirp@0.x, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
||||
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
mkdirp@^1.0.4:
|
||||
mkdirp@^1.0.3, mkdirp@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
@ -9677,7 +9696,7 @@ negotiator@0.6.2:
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
||||
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
|
||||
|
||||
neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1:
|
||||
neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
|
||||
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
|
||||
@ -9730,23 +9749,21 @@ node-forge@^0.10.0:
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
|
||||
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
|
||||
|
||||
node-gyp@^3.8.0:
|
||||
version "3.8.0"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
|
||||
integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==
|
||||
node-gyp@^7.1.0:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae"
|
||||
integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==
|
||||
dependencies:
|
||||
fstream "^1.0.0"
|
||||
glob "^7.0.3"
|
||||
graceful-fs "^4.1.2"
|
||||
mkdirp "^0.5.0"
|
||||
nopt "2 || 3"
|
||||
npmlog "0 || 1 || 2 || 3 || 4"
|
||||
osenv "0"
|
||||
request "^2.87.0"
|
||||
rimraf "2"
|
||||
semver "~5.3.0"
|
||||
tar "^2.0.0"
|
||||
which "1"
|
||||
env-paths "^2.2.0"
|
||||
glob "^7.1.4"
|
||||
graceful-fs "^4.2.3"
|
||||
nopt "^5.0.0"
|
||||
npmlog "^4.1.2"
|
||||
request "^2.88.2"
|
||||
rimraf "^3.0.2"
|
||||
semver "^7.3.2"
|
||||
tar "^6.0.2"
|
||||
which "^2.0.2"
|
||||
|
||||
node-int64@^0.4.0:
|
||||
version "0.4.0"
|
||||
@ -9824,36 +9841,27 @@ node-releases@^1.1.70:
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb"
|
||||
integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==
|
||||
|
||||
node-sass@^4.12.0:
|
||||
version "4.14.1"
|
||||
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.14.1.tgz#99c87ec2efb7047ed638fb4c9db7f3a42e2217b5"
|
||||
integrity sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==
|
||||
node-sass@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-6.0.1.tgz#cad1ccd0ce63e35c7181f545d8b986f3a9a887fe"
|
||||
integrity sha512-f+Rbqt92Ful9gX0cGtdYwjTrWAaGURgaK5rZCWOgCNyGWusFYHhbqCCBoFBeat+HKETOU02AyTxNhJV0YZf2jQ==
|
||||
dependencies:
|
||||
async-foreach "^0.1.3"
|
||||
chalk "^1.1.1"
|
||||
cross-spawn "^3.0.0"
|
||||
cross-spawn "^7.0.3"
|
||||
gaze "^1.0.0"
|
||||
get-stdin "^4.0.1"
|
||||
glob "^7.0.3"
|
||||
in-publish "^2.0.0"
|
||||
lodash "^4.17.15"
|
||||
meow "^3.7.0"
|
||||
mkdirp "^0.5.1"
|
||||
meow "^9.0.0"
|
||||
nan "^2.13.2"
|
||||
node-gyp "^3.8.0"
|
||||
node-gyp "^7.1.0"
|
||||
npmlog "^4.0.0"
|
||||
request "^2.88.0"
|
||||
sass-graph "2.2.5"
|
||||
stdout-stream "^1.4.0"
|
||||
"true-case-path" "^1.0.2"
|
||||
|
||||
"nopt@2 || 3":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
|
||||
integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k=
|
||||
dependencies:
|
||||
abbrev "1"
|
||||
|
||||
nopt@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
|
||||
@ -9861,7 +9869,7 @@ nopt@^5.0.0:
|
||||
dependencies:
|
||||
abbrev "1"
|
||||
|
||||
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0:
|
||||
normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
|
||||
@ -9871,6 +9879,16 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-
|
||||
semver "2 || 3 || 4 || 5"
|
||||
validate-npm-package-license "^3.0.1"
|
||||
|
||||
normalize-package-data@^3.0.0:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e"
|
||||
integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==
|
||||
dependencies:
|
||||
hosted-git-info "^4.0.1"
|
||||
is-core-module "^2.5.0"
|
||||
semver "^7.3.4"
|
||||
validate-npm-package-license "^3.0.1"
|
||||
|
||||
normalize-path@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379"
|
||||
@ -9927,7 +9945,7 @@ npm-run-path@^4.0.0:
|
||||
dependencies:
|
||||
path-key "^3.0.0"
|
||||
|
||||
"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0:
|
||||
npmlog@^4.0.0, npmlog@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
|
||||
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
|
||||
@ -10173,24 +10191,11 @@ os-browserify@^0.3.0:
|
||||
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
|
||||
integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
|
||||
|
||||
os-homedir@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
||||
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
|
||||
|
||||
os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
|
||||
os-tmpdir@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
|
||||
|
||||
osenv@0:
|
||||
version "0.1.5"
|
||||
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
|
||||
integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
|
||||
dependencies:
|
||||
os-homedir "^1.0.0"
|
||||
os-tmpdir "^1.0.0"
|
||||
|
||||
p-each-series@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71"
|
||||
@ -10452,15 +10457,6 @@ path-to-regexp@0.1.7:
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
||||
|
||||
path-type@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
|
||||
integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
path-type@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
|
||||
@ -11195,6 +11191,11 @@ queue-microtask@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||
|
||||
quick-lru@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
|
||||
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
|
||||
|
||||
quill-delta@^3.6.2:
|
||||
version "3.6.3"
|
||||
resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-3.6.3.tgz#b19fd2b89412301c60e1ff213d8d860eac0f1032"
|
||||
@ -11273,14 +11274,6 @@ react-is@^17.0.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
||||
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
|
||||
|
||||
read-pkg-up@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
|
||||
integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=
|
||||
dependencies:
|
||||
find-up "^1.0.0"
|
||||
read-pkg "^1.0.0"
|
||||
|
||||
read-pkg-up@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
|
||||
@ -11306,15 +11299,6 @@ read-pkg-up@^7.0.1:
|
||||
read-pkg "^5.2.0"
|
||||
type-fest "^0.8.1"
|
||||
|
||||
read-pkg@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
|
||||
integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=
|
||||
dependencies:
|
||||
load-json-file "^1.0.0"
|
||||
normalize-package-data "^2.3.2"
|
||||
path-type "^1.0.0"
|
||||
|
||||
read-pkg@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
|
||||
@ -11388,13 +11372,13 @@ realpath-native@^1.1.0:
|
||||
dependencies:
|
||||
util.promisify "^1.0.0"
|
||||
|
||||
redent@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
|
||||
integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=
|
||||
redent@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
|
||||
integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
|
||||
dependencies:
|
||||
indent-string "^2.1.0"
|
||||
strip-indent "^1.0.1"
|
||||
indent-string "^4.0.0"
|
||||
strip-indent "^3.0.0"
|
||||
|
||||
regenerate-unicode-properties@^8.2.0:
|
||||
version "8.2.0"
|
||||
@ -11506,13 +11490,6 @@ repeat-string@^1.6.1:
|
||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
|
||||
|
||||
repeating@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
|
||||
integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=
|
||||
dependencies:
|
||||
is-finite "^1.0.0"
|
||||
|
||||
request-promise-core@1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f"
|
||||
@ -11678,7 +11655,7 @@ rgba-regex@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
|
||||
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
|
||||
|
||||
rimraf@2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3:
|
||||
rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
|
||||
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
|
||||
@ -11802,16 +11779,16 @@ sass-graph@2.2.5:
|
||||
scss-tokenizer "^0.2.3"
|
||||
yargs "^13.3.2"
|
||||
|
||||
sass-loader@^7.1.0:
|
||||
version "7.3.1"
|
||||
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.3.1.tgz#a5bf68a04bcea1c13ff842d747150f7ab7d0d23f"
|
||||
integrity sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==
|
||||
sass-loader@^10:
|
||||
version "10.2.0"
|
||||
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.2.0.tgz#3d64c1590f911013b3fa48a0b22a83d5e1494716"
|
||||
integrity sha512-kUceLzC1gIHz0zNJPpqRsJyisWatGYNFRmv2CKZK2/ngMJgLqxTbXwe/hJ85luyvZkgqU3VlJ33UVF2T/0g6mw==
|
||||
dependencies:
|
||||
clone-deep "^4.0.1"
|
||||
loader-utils "^1.0.1"
|
||||
neo-async "^2.5.0"
|
||||
pify "^4.0.1"
|
||||
semver "^6.3.0"
|
||||
klona "^2.0.4"
|
||||
loader-utils "^2.0.0"
|
||||
neo-async "^2.6.2"
|
||||
schema-utils "^3.0.0"
|
||||
semver "^7.3.2"
|
||||
|
||||
sax@^1.2.4, sax@~1.2.4:
|
||||
version "1.2.4"
|
||||
@ -11850,6 +11827,15 @@ schema-utils@^2.6.5:
|
||||
ajv "^6.12.4"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
schema-utils@^3.0.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
|
||||
integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.8"
|
||||
ajv "^6.12.5"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
scss-tokenizer@^0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
|
||||
@ -11895,7 +11881,7 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semve
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.2.1:
|
||||
semver@^7.2.1, semver@^7.3.4:
|
||||
version "7.3.5"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
||||
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
||||
@ -11909,11 +11895,6 @@ semver@^7.3.2:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@~5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
|
||||
integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
|
||||
|
||||
send@0.17.1:
|
||||
version "0.17.1"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
|
||||
@ -12006,13 +11987,6 @@ sha.js@^2.4.0, sha.js@^2.4.8:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
shallow-clone@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
|
||||
integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
|
||||
dependencies:
|
||||
kind-of "^6.0.2"
|
||||
|
||||
shallow-copy@~0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170"
|
||||
@ -12566,13 +12540,6 @@ strip-ansi@^6.0.0:
|
||||
dependencies:
|
||||
ansi-regex "^5.0.0"
|
||||
|
||||
strip-bom@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
|
||||
integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=
|
||||
dependencies:
|
||||
is-utf8 "^0.2.0"
|
||||
|
||||
strip-bom@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
|
||||
@ -12593,18 +12560,18 @@ strip-final-newline@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
|
||||
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
||||
|
||||
strip-indent@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
|
||||
integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=
|
||||
dependencies:
|
||||
get-stdin "^4.0.1"
|
||||
|
||||
strip-indent@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
|
||||
integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=
|
||||
|
||||
strip-indent@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
|
||||
integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
|
||||
dependencies:
|
||||
min-indent "^1.0.0"
|
||||
|
||||
strip-json-comments@^2.0.0, strip-json-comments@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
@ -12726,14 +12693,17 @@ tapable@^1.0.0, tapable@^1.1.3:
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
||||
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
||||
|
||||
tar@^2.0.0:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40"
|
||||
integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==
|
||||
tar@^6.0.2:
|
||||
version "6.1.11"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
|
||||
integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
|
||||
dependencies:
|
||||
block-stream "*"
|
||||
fstream "^1.0.12"
|
||||
inherits "2"
|
||||
chownr "^2.0.0"
|
||||
fs-minipass "^2.0.0"
|
||||
minipass "^3.0.0"
|
||||
minizlib "^2.1.1"
|
||||
mkdirp "^1.0.3"
|
||||
yallist "^4.0.0"
|
||||
|
||||
terminal-link@^2.0.0:
|
||||
version "2.1.1"
|
||||
@ -12985,10 +12955,10 @@ tr46@^2.0.2:
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
trim-newlines@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
|
||||
integrity sha1-WIeWa7WCpFA6QetST301ARgVphM=
|
||||
trim-newlines@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
|
||||
integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
|
||||
|
||||
"true-case-path@^1.0.2":
|
||||
version "1.0.3"
|
||||
@ -13103,6 +13073,11 @@ type-fest@^0.11.0:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
|
||||
integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
|
||||
|
||||
type-fest@^0.18.0:
|
||||
version "0.18.1"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f"
|
||||
integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==
|
||||
|
||||
type-fest@^0.20.2:
|
||||
version "0.20.2"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||
@ -13951,7 +13926,7 @@ which-module@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
||||
|
||||
which@1, which@^1.2.9, which@^1.3.0:
|
||||
which@^1.2.9, which@^1.3.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
|
||||
@ -14134,6 +14109,11 @@ yargs-parser@^20.2.2:
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.6.tgz#69f920addf61aafc0b8b89002f5d66e28f2d8b20"
|
||||
integrity sha512-AP1+fQIWSM/sMiET8fyayjx/J+JmTPt2Mr0FkrgqB4todtfa53sOsrSAcIrJRD5XS20bKUwaDIuMkWKCEiQLKA==
|
||||
|
||||
yargs-parser@^20.2.3:
|
||||
version "20.2.9"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs@^13.2.2, yargs@^13.2.4, yargs@^13.3.0, yargs@^13.3.2:
|
||||
version "13.3.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
|
||||
|
||||
@ -15,6 +15,7 @@ CREATE TABLE `users` (
|
||||
`language` varchar(4) NOT NULL DEFAULT 'de',
|
||||
`disabled` tinyint DEFAULT '0',
|
||||
`group_id` int unsigned DEFAULT 0,
|
||||
`publisher_id` int DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `email` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include "../SingletonManager/EmailManager.h"
|
||||
#include "../SingletonManager/SessionManager.h"
|
||||
#include "../SingletonManager/LanguageManager.h"
|
||||
|
||||
#include "../tasks/AuthenticatedEncryptionCreateKeyTask.h"
|
||||
|
||||
@ -17,9 +18,12 @@ Poco::JSON::Object* JsonCreateUser::handle(Poco::Dynamic::Var params)
|
||||
std::string password;
|
||||
std::string username;
|
||||
std::string description;
|
||||
std::string language;
|
||||
|
||||
bool login_after_register = false;
|
||||
int emailType;
|
||||
int group_id = 1;
|
||||
int publisher_id = 0;
|
||||
bool group_was_not_set = false;
|
||||
|
||||
auto em = EmailManager::getInstance();
|
||||
@ -40,18 +44,26 @@ Poco::JSON::Object* JsonCreateUser::handle(Poco::Dynamic::Var params)
|
||||
paramJsonObject->get("emailType").convert(emailType);
|
||||
|
||||
auto group_id_obj = paramJsonObject->get("group_id");
|
||||
auto publisher_id_obj = paramJsonObject->get("publisher_id");
|
||||
auto username_obj = paramJsonObject->get("username");
|
||||
auto description_obj = paramJsonObject->get("description");
|
||||
auto language_obj = paramJsonObject->get("language");
|
||||
|
||||
if(!group_id_obj.isEmpty()) {
|
||||
group_id_obj.convert(group_id);
|
||||
}
|
||||
if (!publisher_id_obj.isEmpty()) {
|
||||
publisher_id_obj.convert(publisher_id);
|
||||
}
|
||||
if (!username_obj.isEmpty()) {
|
||||
username_obj.convert(username);
|
||||
}
|
||||
if (!description_obj.isEmpty()) {
|
||||
description_obj.convert(description);
|
||||
}
|
||||
if (!language_obj.isEmpty()) {
|
||||
language_obj.convert(language);
|
||||
}
|
||||
if ((ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS)) {
|
||||
paramJsonObject->get("password").convert(password);
|
||||
}
|
||||
@ -96,15 +108,21 @@ Poco::JSON::Object* JsonCreateUser::handle(Poco::Dynamic::Var params)
|
||||
group_was_not_set = true;
|
||||
}
|
||||
user = controller::User::create(email, first_name, last_name, group_id);
|
||||
auto user_model = user->getModel();
|
||||
if (username.size() > 3) {
|
||||
if (user->isUsernameAlreadyUsed(username)) {
|
||||
return stateError("username already in use");
|
||||
}
|
||||
user->getModel()->setUsername(username);
|
||||
user_model->setUsername(username);
|
||||
}
|
||||
if (description.size() > 3) {
|
||||
user->getModel()->setDescription(description);
|
||||
user_model->setDescription(description);
|
||||
}
|
||||
if (LanguageManager::languageFromString(language) != LANG_NULL) {
|
||||
user_model->setLanguageKey(language);
|
||||
}
|
||||
user_model->setPublisherId(publisher_id);
|
||||
|
||||
auto userModel = user->getModel();
|
||||
Session* session = nullptr;
|
||||
|
||||
|
||||
@ -140,6 +140,9 @@ Poco::JSON::Object* JsonGetUserInfos::handle(Poco::Dynamic::Var params)
|
||||
else if (parameterString == "user.language") {
|
||||
jsonUser.set("language", user_model->getLanguageKey());
|
||||
}
|
||||
else if (parameterString == "user.publisher_id") {
|
||||
jsonUser.set("publisher_id", user_model->getPublisherId());
|
||||
}
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
jsonErrorsArray.add("ask parameter invalid");
|
||||
|
||||
@ -170,10 +170,20 @@ Poco::JSON::Object* JsonUpdateUserInfos::handle(Poco::Dynamic::Var params)
|
||||
}
|
||||
|
||||
}
|
||||
else if ("User.publisher_id" == name) {
|
||||
if (value.isInteger()) {
|
||||
int publisher_id = 0;
|
||||
value.convert(publisher_id);
|
||||
user_model->setPublisherId(publisher_id);
|
||||
extractet_values++;
|
||||
}
|
||||
else {
|
||||
jsonErrorsArray.add("User.publisher_id isn't a valid integer");
|
||||
}
|
||||
}
|
||||
else if ("User.password" == name && (ServerConfig::g_AllowUnsecureFlags & ServerConfig::UNSECURE_PASSWORD_REQUESTS) == ServerConfig::UNSECURE_PASSWORD_REQUESTS)
|
||||
{
|
||||
std::string str_val = validateString(value, "User.password", jsonErrorsArray);
|
||||
|
||||
if (str_val.size() > 0)
|
||||
{
|
||||
if (!user->hasPassword()) {
|
||||
|
||||
@ -15,12 +15,12 @@ namespace model {
|
||||
namespace table {
|
||||
|
||||
User::User()
|
||||
: mPasswordHashed(0), mEmailChecked(false), mLanguageKey("de"), mDisabled(false), mRole(ROLE_NOT_LOADED)
|
||||
: mPasswordHashed(0), mEmailChecked(false), mLanguageKey("de"), mDisabled(false), mPublisherId(0), mRole(ROLE_NOT_LOADED)
|
||||
{
|
||||
}
|
||||
|
||||
User::User(const std::string& email, const std::string& first_name, const std::string& last_name, int group_id, Poco::UInt64 passwordHashed/* = 0*/, std::string languageKey/* = "de"*/)
|
||||
: mFirstName(first_name), mLastName(last_name), mPasswordHashed(passwordHashed), mEmailChecked(false), mLanguageKey(languageKey), mDisabled(false), mGroupId(group_id), mRole(ROLE_NOT_LOADED)
|
||||
: mFirstName(first_name), mLastName(last_name), mPasswordHashed(passwordHashed), mEmailChecked(false), mLanguageKey(languageKey), mDisabled(false), mGroupId(group_id), mPublisherId(0), mRole(ROLE_NOT_LOADED)
|
||||
{
|
||||
setEmail(email);
|
||||
|
||||
@ -29,9 +29,9 @@ namespace model {
|
||||
User::User(UserTuple tuple)
|
||||
: ModelBase(tuple.get<0>()),
|
||||
mFirstName(tuple.get<1>()), mLastName(tuple.get<2>()), mEmail(tuple.get<3>()), mUsername(tuple.get<4>()),
|
||||
mDescription(tuple.get<5>()),
|
||||
mPublicKey(tuple.get<6>()), mCreated(tuple.get<7>()), mEmailChecked(tuple.get<8>()), mDisabled(tuple.get<9>()), mGroupId(tuple.get<10>()),
|
||||
mPasswordHashed(0), mLanguageKey("de"), mRole(ROLE_NOT_LOADED)
|
||||
mDescription(tuple.get<5>()), mPublicKey(tuple.get<6>()), mCreated(tuple.get<7>()),
|
||||
mEmailChecked(tuple.get<8>()), mDisabled(tuple.get<9>()), mGroupId(tuple.get<10>()), mPublisherId(tuple.get<11>()),
|
||||
mPasswordHashed(0), mLanguageKey("de"), mRole(ROLE_NOT_LOADED)
|
||||
{
|
||||
|
||||
}
|
||||
@ -83,12 +83,13 @@ namespace model {
|
||||
|
||||
|
||||
if (mPasswordHashed) {
|
||||
insert << "INSERT INTO users (email, first_name, last_name, username, description, password, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?,?,?);",
|
||||
use(mEmail), use(mFirstName), use(mLastName), use(mUsername), use(mDescription), bind(mPasswordHashed), use(mEmailHash), use(mLanguageKey), use(mGroupId);
|
||||
insert << "INSERT INTO users (email, first_name, last_name, username, description, password, email_hash, language, group_id, publisher_id) VALUES(?,?,?,?,?,?,?,?,?,?);",
|
||||
use(mEmail), use(mFirstName), use(mLastName), use(mUsername), use(mDescription), bind(mPasswordHashed), use(mEmailHash), use(mLanguageKey), use(mGroupId), use(mPublisherId);
|
||||
}
|
||||
else {
|
||||
insert << "INSERT INTO users (email, first_name, last_name, username, description, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?,?);",
|
||||
use(mEmail), use(mFirstName), use(mLastName), use(mUsername), use(mDescription), use(mEmailHash), use(mLanguageKey), use(mGroupId);
|
||||
insert << "INSERT INTO users (email, first_name, last_name, username, description, email_hash, language, group_id, publisher_id) VALUES(?,?,?,?,?,?,?,?,?);",
|
||||
use(mEmail), use(mFirstName), use(mLastName), use(mUsername), use(mDescription), use(mEmailHash), use(mLanguageKey), use(mGroupId), use(mPublisherId);
|
||||
|
||||
}
|
||||
|
||||
return insert;
|
||||
@ -101,13 +102,14 @@ namespace model {
|
||||
_fieldName = getTableName() + std::string(".id");
|
||||
}
|
||||
Poco::Data::Statement select(session);
|
||||
select << "SELECT " << getTableName() << ".id, email, first_name, last_name, username, description, password, pubkey, privkey, email_hash, created, email_checked, language, disabled, group_id, user_roles.role_id "
|
||||
|
||||
select << "SELECT " << getTableName() << ".id, email, first_name, last_name, username, description, password, pubkey, privkey, email_hash, created, email_checked, language, disabled, group_id, publisher_id, user_roles.role_id "
|
||||
<< " FROM " << getTableName()
|
||||
<< " LEFT JOIN user_roles ON " << getTableName() << ".id = user_roles.user_id "
|
||||
<< " WHERE " << _fieldName << " = ?" ,
|
||||
into(mID), into(mEmail), into(mFirstName), into(mLastName), into(mUsername), into(mDescription), into(mPasswordHashed),
|
||||
into(mPublicKey), into(mPrivateKey), into(mEmailHash), into(mCreated), into(mEmailChecked),
|
||||
into(mLanguageKey), into(mDisabled), into(mGroupId), into(mRole);
|
||||
into(mLanguageKey), into(mDisabled), into(mGroupId), into(mPublisherId), into(mRole);
|
||||
|
||||
|
||||
return select;
|
||||
@ -117,9 +119,8 @@ namespace model {
|
||||
{
|
||||
Poco::Data::Statement select(session);
|
||||
// typedef Poco::Tuple<std::string, std::string, std::string, Poco::Nullable<Poco::Data::BLOB>, int> UserTuple;
|
||||
select << "SELECT id, first_name, last_name, email, username, description, pubkey, created, email_checked, disabled, group_id FROM " << getTableName()
|
||||
<< " where " << fieldName << " LIKE ?";
|
||||
|
||||
select << "SELECT id, first_name, last_name, email, username, description, pubkey, created, email_checked, disabled, group_id, publisher_id FROM " << getTableName()
|
||||
<< " where " << fieldName << " LIKE ?";
|
||||
|
||||
return select;
|
||||
}
|
||||
@ -133,8 +134,8 @@ namespace model {
|
||||
}
|
||||
|
||||
// typedef Poco::Tuple<std::string, std::string, std::string, Poco::Nullable<Poco::Data::BLOB>, int> UserTuple;
|
||||
select << "SELECT id, first_name, last_name, email, username, description, pubkey, created, email_checked, disabled, group_id FROM " << getTableName()
|
||||
<< " where " << fieldNames[0] << " LIKE ?";
|
||||
select << "SELECT id, first_name, last_name, email, username, description, pubkey, created, email_checked, disabled, group_id, publisher_id FROM " << getTableName()
|
||||
<< " where " << fieldNames[0] << " LIKE ?";
|
||||
if (conditionType == MYSQL_CONDITION_AND) {
|
||||
for (int i = 1; i < fieldNames.size(); i++) {
|
||||
select << " AND " << fieldNames[i] << " LIKE ? ";
|
||||
@ -245,9 +246,8 @@ namespace model {
|
||||
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
|
||||
|
||||
Poco::Data::Statement update(session);
|
||||
update << "UPDATE users SET first_name = ?, last_name = ?, username = ?, description = ?, disabled = ?, language = ? where id = ?;",
|
||||
use(mFirstName), use(mLastName), use(mUsername), use(mDescription), use(mDisabled), use(mLanguageKey), use(mID);
|
||||
|
||||
update << "UPDATE users SET first_name = ?, last_name = ?, username = ?, description = ?, disabled = ?, language = ?, publisher_id = ? where id = ?;",
|
||||
use(mFirstName), use(mLastName), use(mUsername), use(mDescription), use(mDisabled), use(mLanguageKey), use(mPublisherId), use(mID);
|
||||
|
||||
try {
|
||||
return update.execute();
|
||||
@ -317,6 +317,7 @@ namespace model {
|
||||
ss << "language key: " << mLanguageKey << std::endl;
|
||||
ss << "disabled: " << mDisabled << std::endl;
|
||||
ss << "group id: " << std::to_string(mGroupId) << std::endl;
|
||||
ss << "publisher id: " << std::to_string(mPublisherId) << std::endl;
|
||||
|
||||
mm->releaseMemory(pubkeyHex);
|
||||
mm->releaseMemory(privkeyHex);
|
||||
@ -355,7 +356,8 @@ namespace model {
|
||||
ss << "language key: " << mLanguageKey << "<br>";
|
||||
ss << "role: " << UserRole::typeToString(getRole()) << "<br>";
|
||||
ss << "disabled: " << mDisabled << "<br>";
|
||||
ss << "group_id: " << std::to_string(mGroupId) << std::endl;
|
||||
ss << "group_id: " << std::to_string(mGroupId) << "<br>";
|
||||
ss << "publisher_id" << std::to_string(mPublisherId) << "<br>";
|
||||
|
||||
mm->releaseMemory(pubkeyHex);
|
||||
mm->releaseMemory(email_hash);
|
||||
@ -429,6 +431,7 @@ namespace model {
|
||||
userObj.set("ident_hash", DRMakeStringHash(mEmail.data(), mEmail.size()));
|
||||
userObj.set("language", mLanguageKey);
|
||||
userObj.set("disabled", mDisabled);
|
||||
userObj.set("publisher_id", mPublisherId);
|
||||
try {
|
||||
userObj.set("role", UserRole::typeToString(getRole()));
|
||||
}
|
||||
|
||||
@ -30,7 +30,20 @@ namespace model {
|
||||
USER_FIELDS_LANGUAGE
|
||||
};
|
||||
|
||||
typedef Poco::Tuple<int, std::string, std::string, std::string, std::string, std::string, Poco::Nullable<Poco::Data::BLOB>, Poco::DateTime, int, int, int> UserTuple;
|
||||
typedef Poco::Tuple<
|
||||
int, // id
|
||||
std::string, // first name
|
||||
std::string, // last name
|
||||
std::string, // email
|
||||
std::string, // username
|
||||
std::string, // description
|
||||
Poco::Nullable<Poco::Data::BLOB>, // public key
|
||||
Poco::DateTime, // created
|
||||
int, // check email
|
||||
int, // disabled
|
||||
int, // group_id
|
||||
int // publisher_id
|
||||
> UserTuple;
|
||||
|
||||
class User : public ModelBase
|
||||
{
|
||||
@ -63,6 +76,7 @@ namespace model {
|
||||
inline std::string getNameWithEmailHtml() const { SHARED_LOCK; return mFirstName + " " + mLastName + " <" + mEmail + ">"; }
|
||||
inline const Poco::UInt64 getPasswordHashed() const { SHARED_LOCK; return mPasswordHashed; }
|
||||
inline int getGroupId() const { SHARED_LOCK; return mGroupId; }
|
||||
inline int getPublisherId() const { SHARED_LOCK; return mPublisherId; }
|
||||
inline RoleType getRole() const { SHARED_LOCK; if (mRole.isNull()) return ROLE_NONE; return static_cast<RoleType>(mRole.value()); }
|
||||
inline const unsigned char* getPublicKey() const { SHARED_LOCK; if (mPublicKey.isNull()) return nullptr; return mPublicKey.value().content().data(); }
|
||||
MemoryBin* getPublicKeyCopy() const;
|
||||
@ -93,6 +107,7 @@ namespace model {
|
||||
inline void setLanguageKey(const std::string& languageKey) { UNIQUE_LOCK; mLanguageKey = languageKey; }
|
||||
inline void setDisabled(bool disabled) { UNIQUE_LOCK; mDisabled = disabled; }
|
||||
inline void setGroupId(int groupId) { UNIQUE_LOCK; mGroupId = groupId; }
|
||||
inline void setPublisherId(int publisherId) { UNIQUE_LOCK; mPublisherId = publisherId; }
|
||||
|
||||
Poco::JSON::Object getJson();
|
||||
|
||||
@ -126,6 +141,7 @@ namespace model {
|
||||
bool mDisabled;
|
||||
|
||||
int mGroupId;
|
||||
int mPublisherId;
|
||||
|
||||
// from neighbor tables
|
||||
Poco::Nullable<int> mRole;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
FROM nginx:latest
|
||||
FROM nginx:1.21.0
|
||||
|
||||
WORKDIR /var/www/cakephp
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido",
|
||||
"version": "1.3.1",
|
||||
"version": "1.4.0",
|
||||
"description": "Gradido",
|
||||
"main": "index.js",
|
||||
"repository": "git@github.com:gradido/gradido.git",
|
||||
|
||||
@ -1,21 +1,18 @@
|
||||
#########################################################################################################
|
||||
# Build skeema
|
||||
#########################################################################################################
|
||||
FROM golang:1.14.4 as skeema_build
|
||||
FROM golang:1.17.1 as skeema_build
|
||||
RUN go get -d -v github.com/skeema/skeema
|
||||
WORKDIR /go/src/github.com/skeema/skeema
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /go/bin/skeema .
|
||||
RUN go install github.com/skeema/skeema@v1.5.3
|
||||
|
||||
#########################################################################################################
|
||||
# Run skeema
|
||||
#########################################################################################################
|
||||
FROM alpine:3.13.5 as skeema_run
|
||||
FROM skeema_build as skeema_run
|
||||
|
||||
ENV DOCKER_WORKDIR="/skeema"
|
||||
|
||||
# copy skeema
|
||||
COPY --from=skeema_build /go/bin/skeema /usr/bin/
|
||||
|
||||
|
||||
RUN mkdir -p ${DOCKER_WORKDIR}
|
||||
WORKDIR ${DOCKER_WORKDIR}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user