Merge branch 'master' into vue_with_nginx

This commit is contained in:
einhornimmond 2021-03-25 09:39:53 +01:00
commit f97b7cf709
164 changed files with 12214 additions and 7640 deletions

View File

@ -1,18 +0,0 @@
name: on-push
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build the stack
run: |
git submodule update --init --recursive
docker-compose up -d
- name: Test
env:
GN_INSTANCE_FOLDER: /tmp/gradio-node-instance
GN_CONTAINER_NAME: gradido-node-build
run: docker run gradido-node-test
timeout-minutes: 2

View File

@ -4,34 +4,13 @@ name: gradido test CI
on: [push]
jobs:
##############################################################################
# JOB: PREPARE #####################################################
##############################################################################
#prepare:
# name: Prepare
# runs-on: ubuntu-latest
# # needs: [nothing]
# steps:
# ##########################################################################
# # CHECKOUT CODE ##########################################################
# ##########################################################################
# - name: Checkout code
# uses: actions/checkout@v2
# ##########################################################################
# # TODO: DO STUFF ??? #####################################################
# ##########################################################################
# - name: Check translation files
# run: |
# scripts/translations/sort.sh
# scripts/translations/missing-keys.sh
##############################################################################
# JOB: DOCKER BUILD TEST FRONTEND ############################################
##############################################################################
build_test_frontend:
name: Docker Build Test - Frontend
runs-on: ubuntu-latest
#needs: [nothing] # [prepare]
#needs: [nothing]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
@ -39,7 +18,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# NEO4J ##################################################################
# FRONTEND ###############################################################
##########################################################################
- name: Frontend | Build `test` image
run: |
@ -78,30 +57,58 @@ jobs:
# path: /tmp/backend.tar
##############################################################################
# JOB: DOCKER BUILD TEST WEBAPP ##############################################
# JOB: DOCKER BUILD TEST LOGIN SERVER ########################################
##############################################################################
#build_test_webapp:
# name: Docker Build Test - WebApp
# runs-on: ubuntu-latest
# needs: [prepare]
# steps:
# ##########################################################################
# # CHECKOUT CODE ##########################################################
# ##########################################################################
# - name: Checkout code
# uses: actions/checkout@v2
# ##########################################################################
# # BUILD WEBAPP DOCKER IMAGE (build) ######################################
# ##########################################################################
# - name: webapp | Build `test` image
# run: |
# docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
# docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar
# - name: Upload Artifact
# uses: actions/upload-artifact@v2
# with:
# name: docker-webapp-test
# path: /tmp/webapp.tar
build_test_login_server:
name: Docker Build Test - Login Server
runs-on: ubuntu-latest
#needs: [nothing]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
with:
submodules: recursive
##########################################################################
# BUILD LOGIN SERVER DOCKER IMAGE (build) ################################
##########################################################################
- name: login server | Build `test` image
run: |
docker build --target login_server_debug -t "gradido/loginserver:test" -f ./login_server/Dockerfile.debug login_server/
docker save "gradido/loginserver:test" > /tmp/loginserver.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-loginserver-test
path: /tmp/loginserver.tar
##############################################################################
# JOB: DOCKER BUILD TEST COMMUNITY SERVER ####################################
##############################################################################
build_test_community_server:
name: Docker Build Test - Community Server
runs-on: ubuntu-latest
#needs: [nothing]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# BUILD COMMUNITY SERVER DOCKER IMAGE (build) ############################
##########################################################################
- name: community server | Build `test` image
run: |
docker build -t "gradido/communityserver:test" -f ./community_server/Dockerfile ./
docker save "gradido/communityserver:test" > /tmp/communityserver.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-communityserver-test
path: /tmp/communityserver.tar
##############################################################################
# JOB: LINT FRONTEND #########################################################
@ -207,38 +214,46 @@ jobs:
# run: docker-compose exec -T backend yarn test
##############################################################################
# JOB: UNIT TEST WEBAPP ######################################################
# JOB: UNIT TEST FRONTEND ###################################################
##############################################################################
#unit_test_webapp:
# name: Unit tests - webapp
unit_test_frontend:
name: Unit tests - Frontend
runs-on: ubuntu-latest
needs: [build_test_frontend]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v2
with:
name: docker-frontend-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/frontend.tar
##########################################################################
# UNIT TESTS FRONTEND ####################################################
##########################################################################
- name: frontend | Unit tests
run: docker run --rm gradido/frontend:test yarn run test
#test:
# runs-on: ubuntu-latest
# needs: [build_test_webapp]
# steps:
# ##########################################################################
# # CHECKOUT CODE ##########################################################
# ##########################################################################
# - name: Checkout code
# uses: actions/checkout@v2
# ##########################################################################
# # DOWNLOAD DOCKER IMAGES #################################################
# ##########################################################################
# - name: Download Docker Image (Webapp)
# uses: actions/download-artifact@v2
# with:
# name: docker-webapp-test
# path: /tmp
# - name: Load Docker Image
# run: docker load < /tmp/webapp.tar
# ##########################################################################
# # UNIT TESTS WEBAPP #####################################################
# ##########################################################################
# # TODO: Why do we need those .envs?
# - name: backend | copy env files webapp
# run: cp webapp/.env.template webapp/.env
# - name: backend | copy env files backend
# run: cp backend/.env.template backend/.env
# - name: backend | docker-compose
# run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp
# - name: webapp | Unit tests
# #run: docker run --rm ocelotsocialnetwork/webapp:build yarn run test
# run: docker-compose exec -T webapp yarn test
# - uses: actions/checkout@v2
# - name: Build the stack
# run: |
# git submodule update --init --recursive
# docker-compose up -d
# - name: Test
# env:
# GN_INSTANCE_FOLDER: /tmp/gradio-node-instance
# GN_CONTAINER_NAME: gradido-node-build
# run: docker run gradido-node-test
# timeout-minutes: 2

11
.gitmodules vendored
View File

@ -2,12 +2,12 @@
path = gn
url = https://github.com/gradido/gn.git
branch = master
[submodule "mithril_client"]
path = community_server/mithril_client
url = git@github.com:gradido/gradido_mithrilJS_client.git
#[submodule "mithril_client"]
# path = community_server/mithril_client
# url = https://github.com/gradido/gradido_mithrilJS_client.git
[submodule "src/protobuf"]
path = community_server/src/protobuf
url = git@github.com:gradido/gradido_protocol.git
url = https://github.com/gradido/gradido_protocol.git
[submodule "dependencies/tinf"]
path = login_server/dependencies/tinf
url = https://github.com/jibsen/tinf.git
@ -22,5 +22,4 @@
url = https://github.com/gradido/iroha-ed25519.git
[submodule "dependencies/spirit-po"]
path = login_server/dependencies/spirit-po
url = https://github.com/cbeck88/spirit-po.git
url = https://github.com/cbeck88/spirit-po.git

View File

@ -4,4 +4,25 @@ 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).
#### [0.9.2](https://github.com/gradido/gradido/compare/0.9.1...0.9.2)
- Reload after login fixed [`#50`](https://github.com/gradido/gradido/pull/50)
- Monorepo login server [`#48`](https://github.com/gradido/gradido/pull/48)
- Stage0 [`#3`](https://github.com/gradido/gradido/pull/3)
- Add auto-sign Transaction functionality [`5592275`](https://github.com/gradido/gradido/commit/55922753a7ffd9552be132501d744da491c409b5)
- read in login the real client ip X-Real-IP from nginx forwarded not from community server [`512d307`](https://github.com/gradido/gradido/commit/512d307a19b955bb6e26ae8b274def354829b50f)
- move check if all passwords allow direct into pwdValidation so it will work with every code which ask for password [`e2c38c1`](https://github.com/gradido/gradido/commit/e2c38c1a0fc25a4a2bc922c4bbc44d86b6d00d8b)
#### 0.9.1
> 13 March 2021
- Login gbb [`#12`](https://github.com/gradido/gradido/pull/12)
- Admin prework axios json [`#10`](https://github.com/gradido/gradido/pull/10)
- Merge pull request #1 from gradido/master [`#8`](https://github.com/gradido/gradido/pull/8)
- [WIP] 2 create a dockerfile for the frontend application [`#6`](https://github.com/gradido/gradido/pull/6)
- Master - first step [`#1`](https://github.com/gradido/gradido/pull/1)
- Add docker compose [`#7`](https://github.com/gradido/gradido/pull/7)
- style 404 side :) [`c7bdf89`](https://github.com/gradido/gradido/commit/c7bdf8978594b932615e48f9bb1c19d3c3bf3fcf)
- publish workflow test [`df6f66f`](https://github.com/gradido/gradido/commit/df6f66ffe70baa9ed3f70b460a6c0c14011bb944)
- many translations. translation structure [`bf68547`](https://github.com/gradido/gradido/commit/bf685479767d19c246c4d6abe3577dc3cb666346)

@ -1 +0,0 @@
Subproject commit f0b1d113cee2a76e9dbb098b315f4acaf38410d0

View File

@ -55,7 +55,7 @@ class JsonRequestClientComponent extends Component
]), '/getRunningUserTasks');
}
public function getUsers($session_id, $searchString)
public function getUsers($session_id, $searchString, $accountState)
{
if($searchString == "") {
return ['state' => 'error', 'type' => 'parameter error', 'msg' => 'search string is empty'];
@ -66,7 +66,8 @@ class JsonRequestClientComponent extends Component
return $this->sendRequest(json_encode([
'session_id' => $session_id,
'search' => $searchString
'search' => $searchString,
'account_state' => $accountState,
]), '/getUsers');
}

View File

@ -197,7 +197,7 @@ class StateBalancesController extends AppController
}
uasort($transactions, array($this, 'sortTransactions'));
$this->set('transactions', $transactions);
$this->set('transactionExecutingCount', $session->read('Transaction.executing'));
$this->set('transactionExecutingCount', $session->read('Transactions.executing'));
$this->set('balance', $session->read('StateUser.balance'));
$this->set('timeUsed', microtime(true) - $startTime);
$this->set('gdtSum', $gdtSum);
@ -370,7 +370,7 @@ class StateBalancesController extends AppController
return $this->returnJson([
'state' => 'success',
'transactions' => $transactions,
'transactionExecutingCount' => $session->read('Transaction.executing'),
'transactionExecutingCount' => $session->read('Transactions.executing'),
'count' => count($transactions),
'gdtSum' => $gdtSum,
'timeUsed' => microtime(true) - $startTime

View File

@ -196,7 +196,7 @@ class StateUserTransactionsController extends AppController
return $this->returnJson([
'state' => 'success',
'transactions' => $transactions,
'transactionExecutingCount' => $session->read('Transaction.executing'),
'transactionExecutingCount' => $session->read('Transactions.executing'),
'count' => $all_user_transactions_count,
'timeUsed' => microtime(true) - $startTime
]);

View File

@ -99,9 +99,14 @@ class StateUsersController extends AppController
//$this->set('timeUsed', $timeUsed);
$csfr_token = $this->request->getParam('_csrfToken');
$this->set(compact('timeUsed', 'searchForm', 'csfr_token'));
$empty_string = '... empty ...';
if ($this->request->is('post')) {
$finalUserEntrys = [];
$requestData = $this->request->getData();
$account_state = $requestData['account_state'];
if($requestData['search'] == '' && $account_state != 'all') {
$requestData['search'] = $empty_string;
}
if ($searchForm->validate($requestData)) {
//var_dump($requestData);
@ -111,7 +116,7 @@ class StateUsersController extends AppController
$searchType = 'email';
}
// find users on login server
$resultJson = $this->JsonRequestClient->getUsers($session->read('session_id'), $searchString);
$resultJson = $this->JsonRequestClient->getUsers($session->read('session_id'), $searchString, $account_state);
$loginServerUser = [];
if ($resultJson['state'] == 'success') {
$dataJson = $resultJson['data'];
@ -139,28 +144,42 @@ class StateUsersController extends AppController
}
}
// find user on community server db
$globalSearch = '%' . $searchString . '%';
$communityUsers = $this->StateUsers
->find('all')
->contain(['StateBalances' => ['fields' => ['amount', 'state_user_id']]]);
$communityUsers->where(['OR' => [
'first_name LIKE' => $globalSearch,
'last_name LIKE' => $globalSearch,
//'username LIKE' => $globalSearch,
'email LIKE' => $globalSearch
]]);
//var_dump($communityUsers->toArray());
foreach ($communityUsers as $u) {
$pubkey_hex = bin2hex(stream_get_contents($u->public_key));
$u->public_hex = $pubkey_hex;
if (!isset($pubkeySorted[$pubkey_hex])) {
$pubkeySorted[$pubkey_hex] = ['login' => [], 'community' => []];
if($account_state == 'email not activated') {
if(count($pubkeySorted) > 0) {
$communityUsers->where(['hex(public_key) IN' => array_keys($pubkeySorted)]);
} else {
$communityUsers = null;
}
array_push($pubkeySorted[$pubkey_hex]['community'], $u);
} else {
$globalSearch = '%' . $searchString . '%';
$communityUsers->where(['OR' => [
'first_name LIKE' => $globalSearch,
'last_name LIKE' => $globalSearch,
//'username LIKE' => $globalSearch,
'email LIKE' => $globalSearch
]]);
}
$finalUserEntrys = [];
//var_dump($communityUsers->toArray());
if($communityUsers) {
foreach ($communityUsers as $u) {
$pubkey_hex = bin2hex(stream_get_contents($u->public_key));
$u->public_hex = $pubkey_hex;
if (!isset($pubkeySorted[$pubkey_hex])) {
$pubkeySorted[$pubkey_hex] = ['login' => [], 'community' => []];
}
array_push($pubkeySorted[$pubkey_hex]['community'], $u);
}
}
// detect states
foreach ($pubkeySorted as $pubhex => $user) {
$finalUser = [];

View File

@ -323,7 +323,7 @@ class TransactionCreationsController extends AppController
$this->set('firstDayLastMonth', $firstDayLastMonth);
$this->set('activeUser', $user);
$this->set('creationForm', $creationForm);
$this->set('transactionExecutingCount', $session->read('Transaction.executing'));
$this->set('transactionExecutingCount', $session->read('Transactions.executing'));
$this->set('timeUsed', microtime(true) - $startTime);
$this->set('countUsers', $countUsers);
$this->set('limit', $limit);

View File

@ -11,7 +11,9 @@ class UserSearchForm extends Form
protected function _buildSchema(Schema $schema)
{
return $schema->addField('search', ['type' => 'string']);
return $schema
->addField('search', ['type' => 'string'])
->addField('account_state', ['type' => 'select']);
}
function validationDefault(Validator $validator)

View File

@ -11,6 +11,19 @@ $this->assign('title', __('Benutzer suchen'));
$this->loadHelper('Form', [
'templates' => 'horizontal_form',
]);
$stateOptions = [
'all' => __('Alle'),
//'account created'=>__('Konto angelegt'),
//'account not on login-server' => __('Konto nicht auf Login-Server'),
//'email activated' => __('Konto aktiviert'),
//'account copied to community' => __('Konto auf Gemeinschafts-Server'),
'email not activated' => __('Konto nicht aktiviert'),
//'account multiple times on login-server' => __('Konto mehrfach vorhanden'),
//'account not on community server' => __('Konto nicht auf Gemeinschafts-Server'),
//'no keys' => __('Keine Schlüssel generiert')
];
?>
<?= $this->Html->css([
'loginServer/style.css',
@ -41,7 +54,8 @@ $this->loadHelper('Form', [
<p class="form-header">Benutzer suchen</p>
<div class="form-body">
<?= $this->Form->create($searchForm, []) ?>
<?= $this->Form->control('search', ['label' => __('Suchbegriff'), 'class' => 'form-control', 'id' => 'inlineFormInputGroup', 'placeholder' => __('Vorname/Nachname/E-Mail')]) ?>
<?= $this->Form->control('search', ['label' => __('Suchbegriff'), 'class' => 'form-control', 'id' => 'inlineFormInputGroup', 'placeholder' => __('Vorname/Nachname/E-Mail'), 'required' => false]) ?>
<?= $this->Form->control('account_state', ['label' => __('Konto Status'), 'class' => 'form-control', 'type' => 'select', 'options' => $stateOptions]) ?>
<?= $this->Form->button('<i class="material-icons-outlined">search</i>&nbsp;' . __('Suchen'), ['class' => 'form-button']) ?>
<?= $this->Form->hidden('order_row', ['id' => 'input-order-row']) ?>
</div>
@ -59,7 +73,7 @@ $this->loadHelper('Form', [
csfr_token = '<?= $csfr_token ?>';
</script>
<?= $this->Html->script('userSearch') ?>
<?= $this->Html->script('userSearch.min') ?>
<!-- npm run build im mithril client! -->
<!-- keybase://team/gradido/gradido_mithril_user_search -->

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -15,8 +15,10 @@ mit:
```ini
unsercure.allow_cors_all = 1
```
Wird bei allen JSON-Requests zum Header: Access-Control-Allow-Origin:*
hinzugefügt.
Wird bei allen JSON-Requests zum Header hinzugefügt:
- Access-Control-Allow-Origin:*
- Access-Control-Allow-Headers: "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"
In diesen Beispielen gehe ich jetzt davon aus, das du das gesamte Gradido Projekt mit Docker gebaut hast und auf dem lokalen Rechner laufen lässt.
@ -81,4 +83,130 @@ data: {"session_id": -127182}
Wenn alles okay ist erhältst du:
```json
{"state":"success"}
```
```
## Update User Data
Update first name, last name, user language and enable/disable user
Language currently supported de and en
User will be disabled if he wants a account delete but has transactions.
Until transactions are saved in real blockchain, we need this data because the public key
is in db only saved in state_users so we wenn delete this entry, validating all transactions not longer possible.
Disabled User cannot login and cannot receive transactions.
In update Object only one of the sets needs to be there.
Update password can only be used if in Login-Server config:
```ini
unsecure.allow_passwort_via_json_request = 1
```
POST http://localhost/login_api/updateUserInfos
```json
{"session_id": -127182, "email": "max.musterman@gmail.de", "update": {
"User.first_name": "Max",
"User.last_name" : "Musterman",
"User.disabled": 0,
"User.language": "de"
"User.password": "1234"
}
}
```
also valid
```json
{"session_id": -127182, "email": "max.musterman@gmail.de", "update": {
"User.last_name" : "Musterman"
}
}
```
It returns if everything is okay
```json
{"state":"success", "valid_values": 4, "errors":[]}
```
- valid_values: should contain count of entrys in update if no error occured (User.password will not be counted)
- errors: contain on error string for every entry in update, which type isn't like expected
- password:
- "new password is the same as old password": no change taking place
- "password changed, coludn"t load private key for re-encryption": password was successfully changed, is at the moment only a warning as long as user_backups are unencrypted, safe to ignore
- "stored pubkey and private key didn't match": error by re-encryption keys, no changes saved
- "User.password isn't valid": if password validation failed, followed by reasons why (additional array in array)
example:
```json
{"errors":[
"User.password isn't valid",[
"Passwort: Dein Passwort ist zu kurz!\n",
"Passwort: Bitte gebe ein g&uuml;ltiges Password ein mit mindestens 8 Zeichen, Gro&szlig;- und Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen (@$!%*?&+-_) ein!\n"
]
],
"state":"success",
"valid_values":0
}
```
## Retrieve User Data
Retrieve different user data, in ask only one field is needed, or every possible combination
from the available fields
Normal User can only retrieve data for himself, admins (login-server admin) can retrieve data from every user
Email is also the email address of user from which data are asked
POST http://localhost/login_api/getUserInfos
```json
{"session_id": -127182, "email": "max.musterman@gmail.de", "ask": [
"EmailVerificationCode.Register",
"loginServer.path",
"user.pubkeyhex",
"user.first_name",
"user.last_name",
"user.disabled",
"user.email_checked",
]
}
```
returns if no error occured:
```json
{"state": "success", "userData": {
"EmailVerificationCode.Register": "2718271129122",
"pubkeyhex": "131c7f68dd94b2be4c913400ff7ff4cdc03ac2bda99c2d29edcacb3b065c67e6",
"first_name": "Max",
"last_name": "Musterman",
"disabled": 0,
"email_checked": 1
}, "server": {
"loginServer.path": "http://localhost/account"
},
"errors": []
}
```
Return only the fields which are defined in ask
- EmailVerificationCode.Register: return the email verification code for check email (create one if none exist), work only if logged in user is admin and the email isn't from him
- loginServer.path: the redirect path to login-server, for example for login with login-server html frontend
- user.pubkeyhex: public key of user in hex-format
- user.first_name: first name of user
- user.last_name: last name of user
- user.disabled: User will be disabled if he wants a account delete but has transactions.
Until transactions are saved in real blockchain, we need this data because the public key
is in db only saved in state_users so we wenn delete this entry, validating all transactions not 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
- errors: array of strings if error occure
## Login by Email Verification Code
Used for replace http://localhost/account/checkEmail
Can be used to set check_email to 1 (will be done automaticly if called with valid email verification code of type register or registerDirect)
Can be used for password reset (additional step required: call update user info with new password)
GET http://localhost/login_api/loginViaEmailVerificationCode?emailVerificationCode=382738273892983
return
```json
{"state":"success", "email_verification_code_type":"resetPassword","info":[],"session_id":1853761475}
```
- email_verification_code_type
- resetPassword: for password resets, will be deleted immediately, is a only one use code
- registerDirect: code generated by register for check email
- register: code generated by auto-register via elopage for check email
- info can contain additional info strings
- user hasn't password: if user hasn't set a password yet (for example if he was registered via elopage)
- email already activated: if email was already checked
- session_id: session_id for new session

22
frontend/.babelrc Normal file
View File

@ -0,0 +1,22 @@
{
"presets": [
[
"@babel/preset-env"
]
],
"env": {
"test": {
"plugins": ["require-context-hook"],
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "10"
}
}
]
]
}
}
}

3
frontend/.eslintignore Normal file
View File

@ -0,0 +1,3 @@
node_modules
**/*.min.js
dist

View File

@ -1,14 +1,30 @@
module.exports = {
root: true,
env: {
node: true
},
extends: ['plugin:vue/essential'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
browser: true,
node: true,
jest: true
},
parserOptions: {
parser: 'babel-eslint'
},
extends: [
'plugin:vue/essential',
'plugin:prettier/recommended'
],
// required to lint *.vue files
plugins: [
'vue',
'prettier',
'jest'
],
// add your custom rules here
rules: {
'no-console': ['error'],
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'vue/component-name-in-template-casing': ['error', 'kebab-case'],
'prettier/prettier': ['error', {
htmlWhitespaceSensitivity: 'ignore'
}],
}
};
}

2
frontend/.gitignore vendored
View File

@ -18,3 +18,5 @@ package-lock.json
*.ntvs*
*.njsproj
*.sln
*~

8
frontend/.prettierrc.js Normal file
View File

@ -0,0 +1,8 @@
module.exports = {
semi: false,
printWidth: 100,
singleQuote: true,
trailingComma: "all",
tabWidth: 2,
bracketSpacing: true
};

View File

@ -1,14 +1,12 @@
module.exports = {
"presets": [
"@vue/app"
],
"plugins": [
presets: ['@vue/app'],
plugins: [
[
"component",
'component',
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
libraryName: 'element-ui',
styleLibraryName: 'theme-chalk',
},
],
],
}

View File

@ -1,12 +1,12 @@
// This configuration file is not used anywhere in the code, it's a hack to handle InteliJ relative path imports
// Keep in sync with actual webpack aliases
const path = require('path');
const path = require('path')
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
};
'@': path.resolve(__dirname, 'src'),
},
},
}

25
frontend/jest.config.js Normal file
View File

@ -0,0 +1,25 @@
module.exports = {
verbose: true,
collectCoverageFrom: ['**/*.{js,vue}', '!**/node_modules/**', '!**/?(*.)+(spec|test).js?(x)'],
moduleFileExtensions: [
'js',
//'jsx',
'json',
'vue',
],
coverageReporters: ['lcov'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
transform: {
'^.+\\.vue$': 'vue-jest',
// '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
'^.+\\.(js|jsx)?$': 'babel-jest',
},
//setupFiles: [
// "<rootDir>/test/registerContext.js"
//],
testMatch: ['**/?(*.)+(spec|test).js?(x)'],
// snapshotSerializers: ['jest-serializer-vue'],
transformIgnorePatterns: ['<rootDir>/node_modules/'],
}

View File

@ -1,17 +1,22 @@
{
"name": "bootstrap-vue-gradido-wallet",
"version": "0.9.1",
"version": "0.9.2",
"private": true,
"scripts": {
"start": "node server.js",
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"lint": "eslint --ext .js,.vue .",
"dev": "yarn run serve",
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'"
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'",
"test": "jest"
},
"dependencies": {
"@vue/test-utils": "^1.1.3",
"axios": "^0.21.1",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^26.6.3",
"babel-plugin-require-context-hook": "^1.0.0",
"bootstrap": "4.3.1",
"bootstrap-vue": "^2.5.0",
"chart.js": "^2.9.3",
@ -22,18 +27,29 @@
"element-ui": "2.4.11",
"es6-promise": "^4.1.1",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^7.5.0",
"eslint-config-prettier": "^8.1.0",
"eslint-config-standard": "^16.0.2",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.3.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-standard": "^5.0.0",
"eslint-plugin-vue": "^7.8.0",
"express": "^4.17.1",
"flatpickr": "^4.5.7",
"fuse.js": "^3.2.0",
"google-maps": "^3.2.1",
"jest": "^26.6.3",
"nouislider": "^12.1.0",
"particles-bg-vue": "1.2.3",
"perfect-scrollbar": "^1.3.0",
"prettier": "^2.2.1",
"qrcode": "^1.4.4",
"quill": "^1.3.6",
"sweetalert2": "^9.5.4",
"vee-validate": "^3.2.1",
"vee-validate": "^3.4.5",
"vue": "^2.6.11",
"vue-bootstrap-typeahead": "^0.2.6",
"vue-chartjs": "^3.5.0",
@ -44,6 +60,7 @@
"vue-flatpickr-component": "^8.1.2",
"vue-good-table": "^2.21.3",
"vue-i18n": "^8.22.4",
"vue-jest": "^3.0.7",
"vue-moment": "^4.1.0",
"vue-qrcode": "^0.3.5",
"vue-qrcode-reader": "^2.3.16",
@ -73,6 +90,6 @@
"last 2 versions",
"not ie <= 10"
],
"author": "Bernd Hückstädt - https://www.gradido.net/",
"description": "Gradido Wallet"
"author": "Gradido-Akademie - https://www.gradido.net/",
"description": "Gradido, the Natural Economy of Life, is a way to worldwide prosperity and peace in harmony with nature. - Gradido, die Natürliche Ökonomie des lebens, ist ein Weg zu weltweitem Wohlstand und Frieden in Harmonie mit der Natur."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,11 +1,11 @@
<!--
=========================================================
* Gradido Wallet - v0.0.1
* Gradido Konto - v0.0.1
=========================================================
* Product Page: https://www.gradido.net
* Copyright 2020 Bernd Hückstädt - Gradido (https://www.gradido.net)
* Copyright 2021 Gradido-Akademie - (https://www.gradido.net)
* Coded by www.gradido.net
@ -21,7 +21,7 @@
<link rel="icon" type="image/png" sizes="96x96" href="<%= webpackConfig.output.publicPath %>favicon.png">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>Gradido Wallet</title>
<title>Gradido Account</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<!-- Fonts -->

View File

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

View File

@ -1,37 +1,53 @@
<template>
<div id="app" class="font-sans text-gray-800">
<header class="">
<div id="app" class="font-sans text-gray-800">
<header>
<b-col class="text-center">
<b-dropdown
size="sm"
split
variant="secondary"
:text="$t('language') + ' - ' + $i18n.locale"
class="m-md-2"
>
<b-dropdown-divider></b-dropdown-divider>
<b-dropdown-item @click.prevent="setLocale('de')">Deutsch</b-dropdown-item>
<b-dropdown-item @click.prevent="setLocale('en')">English</b-dropdown-item>
</b-dropdown>
</b-col>
</header>
<div class="">
<particles-bg type="custom" :config="config" :bg="true" />
<div class="">
<particles-bg type="custom" :config="config" :bg="true" />
<router-view />
</div>
</div>
</div>
</template>
<script>
import { ParticlesBg } from "particles-bg-vue";
import icon from "./icon.js";
import { ParticlesBg } from 'particles-bg-vue'
import icon from './icon.js'
import { localeChanged } from 'vee-validate'
export default {
name: 'app',
components: {
ParticlesBg
},
created () {
//console.log("this.$cookies.get('gdd_session_id') ", this.$cookies.get('gdd_session_id') )
console.log(" $cookies.isKey('gdd_session_id') ", this.$cookies.isKey("gdd_session_id") )
if ( this.$cookies.isKey('gdd_session_id') ) {
//this.$store.state.email = this.$cookies.get('gdd_u')
console.log("login to kontooverview")
this.$router.push('/KontoOverview')
}else {
console.log("login to LOGIN")
this.$router.push("/Login")
}
},
data() {
ParticlesBg,
},
created() {
//console.log('xx', $cookies.get('gdd_lang'))
//console.log('%cWillkommen bei Gradido %cgreen text', 'font-weight:bold', 'color: green')
if ($cookies.isKey('gdd_session_id') == true) {
//console.log('%cHey %c' + $cookies.get('gdd_u') + '', 'font-weight:bold', 'color: orange')
this.$store.commit('session_id', $cookies.get('gdd_session_id'))
this.$store.commit('email', $cookies.get('gdd_u'))
this.$store.commit('language', $cookies.get('gdd_lang'))
this.$i18n.locale = $cookies.get('gdd_lang')
this.$router.push('overview')
} else {
//console.log('app.vue to Logout')
this.$store.dispatch('logout')
}
},
data() {
return {
config: {
num: [1, 7],
@ -43,33 +59,64 @@ export default {
body: icon,
alpha: [0.6, 0],
scale: [0.1, 0.4],
position: "all",
cross: "dead",
position: 'all',
cross: 'dead',
random: 2,
},
};
}
},
methods: {
setLocale(locale) {
this.$i18n.locale = locale
this.$store.commit('language', this.$i18n.locale)
localeChanged(locale)
},
},
}
</script>
<style>
.btn {
background-color: #5A7B02;
border-color: #5e72e4;
}
a, .copyright{
color: #5A7B02;
}
gradido-global-color-text{color: #3D443B }
gradido-global-color-accent{color: #047006 }
gradido-global-color-6e0a9c9e{color: #000 }
gradido-global-color-2d0fb154{color: #047006 }
gradido-global-color-16efe88c{color: #7EBC55 }
gradido-global-color-1939326{color: #F6FFF6 }
gradido-global-color-9d79fc1{color: #047006 }
gradido-global-color-6347f4d{color: #5A7B02 }
gradido-global-color-4fbc19a{color: #014034 }
gradido-global-color-d341874{color: #B6D939 }
gradido-global-color-619d338{color: #8EBFB1 }
gradido-global-color-44819a9{color: #026873 }
.btn-primary pim {
background-color: #5a7b02;
border-color: #5e72e4;
}
a,
.copyright {
color: #5a7b02;
}
gradido-global-color-text {
color: #3d443b;
}
gradido-global-color-accent {
color: #047006;
}
gradido-global-color-6e0a9c9e {
color: #000;
}
gradido-global-color-2d0fb154 {
color: #047006;
}
gradido-global-color-16efe88c {
color: #7ebc55;
}
gradido-global-color-1939326 {
color: #f6fff6;
}
gradido-global-color-9d79fc1 {
color: #047006;
}
gradido-global-color-6347f4d {
color: #5a7b02;
}
gradido-global-color-4fbc19a {
color: #014034;
}
gradido-global-color-d341874 {
color: #b6d939;
}
gradido-global-color-619d338 {
color: #8ebfb1;
}
gradido-global-color-44819a9 {
color: #026873;
}
</style>

View File

@ -1,33 +1,33 @@
import axios from 'axios';
import axios from 'axios'
import CONFIG from '../config'
const apiGet = async (url) => {
try {
const result = await axios.get(url);
if(result.status !== 200){
throw new Error('HTTP Status Error '+result.status)
const result = await axios.get(url)
if (result.status !== 200) {
throw new Error('HTTP Status Error ' + result.status)
}
if(result.data.state !== 'success'){
if (result.data.state !== 'success') {
throw new Error(result.data.msg)
}
return { success: true, result }
} catch(error){
return { success: false, result: error}
} catch (error) {
return { success: false, result: error }
}
}
const apiPost = async (url, payload) => {
try {
const result = await axios.post(url, payload);
if(result.status !== 200){
throw new Error('HTTP Status Error '+result.status)
const result = await axios.post(url, payload)
if (result.status !== 200) {
throw new Error('HTTP Status Error ' + result.status)
}
if(result.data.state !== 'success'){
if (result.data.state !== 'success') {
throw new Error(result.data.msg)
}
return { success: true, result }
} catch(error){
return { success: false, result: error}
} catch (error) {
return { success: false, result: error }
}
}
@ -38,17 +38,27 @@ const communityAPI = {
transactions: async (session_id) => {
return apiGet(CONFIG.COMMUNITY_API_STATE_BALANCE_URL + 'ajaxListTransactions/' + session_id)
},
create: async (session_id, email, amount, memo, target_date = new Date() ) => {
/*create: async (session_id, email, amount, memo, target_date = new Date() ) => {
const payload = {
session_id,
email,
amount,
target_date,
target_date,
memo,
auto_sign: true
auto_sign: true,
}
return apiPost(CONFIG.COMMUNITY_API_TRANSACTION_CREATION_URL + 'ajaxCreate/', payload)
}
},*/
send: async (session_id, email, amount, memo) => {
const payload = {
session_id,
email,
amount,
memo,
auto_sign: true,
}
return apiPost(CONFIG.COMMUNITY_API_TRANSACTION_SEND_COINS + 'ajaxCreate/', payload)
},
}
export default communityAPI
export default communityAPI

View File

@ -1,4 +1,4 @@
import axios from 'axios';
import axios from 'axios'
import CONFIG from '../config'
// control email-text sended with email verification code
@ -9,16 +9,16 @@ const EMAIL_TYPE = {
const apiPost = async (url, payload) => {
try {
const result = await axios.post(url, payload);
if(result.status !== 200){
throw new Error('HTTP Status Error '+result.status)
const result = await axios.post(url, payload)
if (result.status !== 200) {
throw new Error('HTTP Status Error ' + result.status)
}
if(result.data.state !== 'success'){
if (result.data.state !== 'success') {
throw new Error(result.data.msg)
}
return { success: true, result }
} catch(error){
return { success: false, result: error}
} catch (error) {
return { success: false, result: error }
}
}
@ -31,20 +31,20 @@ const loginAPI = {
return apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', payload)
},
logout: async (session_id) => {
const payload= { session_id }
const payload = { session_id }
return apiPost(CONFIG.LOGIN_API_URL + 'logout', payload)
},
create : async (email, first_name, last_name, password) => {
create: async (email, first_name, last_name, password) => {
const payload = {
email,
first_name,
last_name,
password,
emailType: EMAIL_TYPE.DEFAULT,
login_after_register: true
login_after_register: true,
}
return apiPost(CONFIG.LOGIN_API_URL + 'createUser', payload)
},
},
}
export default loginAPI
export default loginAPI

View File

@ -1,50 +1,45 @@
<template>
<b-badge
:variant="type"
:pill="rounded"
:size="size"
:class="{'badge-circle': circle }">
<b-badge :variant="type" :pill="rounded" :size="size" :class="{ 'badge-circle': circle }">
<slot>
<i v-if="icon" :class="icon"></i>
</slot>
</b-badge>
</template>
<script>
export default {
name: "badge",
props: {
tag: {
type: String,
default: "span",
description: "Html tag to use for the badge."
},
rounded: {
type: Boolean,
default: false,
description: "Whether badge is of pill type"
},
circle: {
type: Boolean,
default: false,
description: "Whether badge is circle"
},
icon: {
type: String,
default: "",
description: "Icon name. Will be overwritten by slot if slot is used"
},
type: {
type: String,
default: "default",
description: "Badge type (primary|info|danger|default|warning|success)"
},
size: {
type: String,
description: 'Badge size (md, lg)',
default: ''
}
}
};
export default {
name: 'badge',
props: {
tag: {
type: String,
default: 'span',
description: 'Html tag to use for the badge.',
},
rounded: {
type: Boolean,
default: false,
description: 'Whether badge is of pill type',
},
circle: {
type: Boolean,
default: false,
description: 'Whether badge is circle',
},
icon: {
type: String,
default: '',
description: 'Icon name. Will be overwritten by slot if slot is used',
},
type: {
type: String,
default: 'default',
description: 'Badge type (primary|info|danger|default|warning|success)',
},
size: {
type: String,
description: 'Badge size (md, lg)',
default: '',
},
},
}
</script>
<style>
</style>
<style></style>

View File

@ -8,7 +8,6 @@
>
<slot v-if="!dismissible"></slot>
<template v-else>
<template v-if="icon || $slots.icon">
<slot name="icon">
<span class="alert-icon" data-notify="icon">
@ -17,14 +16,16 @@
</slot>
</template>
<span class="alert-text"> <slot></slot> </span>
<span class="alert-text"><slot></slot></span>
<slot name="dismiss-icon">
<button type="button"
class="close"
data-dismiss="alert"
aria-label="Close"
@click="dismissAlert">
<button
type="button"
class="close"
data-dismiss="alert"
aria-label="Close"
@click="dismissAlert"
>
<span aria-hidden="true">×</span>
</button>
</slot>
@ -33,39 +34,43 @@
</fade-transition>
</template>
<script>
import { FadeTransition } from 'vue2-transitions';
import { FadeTransition } from 'vue2-transitions'
export default {
name: 'base-alert',
components: {
FadeTransition
export default {
name: 'base-alert',
components: {
FadeTransition,
},
created() {
//console.log('base-alert gesetzt in =>', this.$route.path)
},
props: {
type: {
type: String,
default: 'default',
description: 'Alert type',
},
props: {
type: {
type: String,
default: 'default',
description: 'Alert type'
},
dismissible: {
type: Boolean,
default: false,
description: 'Whether alert is dismissible (closeable)'
},
icon: {
type: String,
default: '',
description: 'Alert icon to display'
}
dismissible: {
type: Boolean,
default: false,
description: 'Whether alert is dismissible (closeable)',
},
data() {
return {
visible: true
};
icon: {
type: String,
default: '',
description: 'Alert icon to display',
},
methods: {
dismissAlert() {
this.visible = false;
}
},
data() {
return {
visible: true,
}
};
},
methods: {
dismissAlert() {
this.visible = false
this.$store.state.loginfail = false
},
},
}
</script>

View File

@ -12,7 +12,7 @@
{ 'btn-wd': wide },
{ 'btn-icon btn-fab': icon },
{ 'btn-link': link },
{ disabled: disabled }
{ disabled: disabled },
]"
>
<slot name="loading">
@ -34,42 +34,42 @@ export default {
type: {
type: String,
default: 'default',
description: 'Button type (primary|secondary|danger etc)'
description: 'Button type (primary|secondary|danger etc)',
},
nativeType: {
type: String,
default: 'button',
description: 'Button native type (e.g button, input etc)'
description: 'Button native type (e.g button, input etc)',
},
size: {
type: String,
default: '',
description: 'Button size (sm|lg)'
description: 'Button size (sm|lg)',
},
outline: {
type: Boolean,
description: 'Whether button is outlined (only border has color)'
description: 'Whether button is outlined (only border has color)',
},
link: {
type: Boolean,
description: 'Whether button is a link (no borders or background)'
}
description: 'Whether button is a link (no borders or background)',
},
},
methods: {
handleClick(evt) {
this.$emit('click', evt);
}
}
};
this.$emit('click', evt)
},
},
}
</script>
<style lang="scss">
.base-button {
display: inline-flex;
align-items: center;
justify-content: center;
.base-button {
display: inline-flex;
align-items: center;
justify-content: center;
i {
padding: 0 3px;
}
i {
padding: 0 3px;
}
}
</style>

View File

@ -9,22 +9,19 @@
<component
:is="titleTag"
class="btn-rotate"
:class="[{'dropdown-toggle': hasToggle}, titleClasses]"
:class="[{ 'dropdown-toggle': hasToggle }, titleClasses]"
:aria-expanded="isOpen"
data-toggle="dropdown"
>
<slot name="title" :is-open="isOpen">
<i :class="icon"></i> {{ title }}
<i :class="icon"></i>
{{ title }}
</slot>
</component>
</slot>
<ul
class="dropdown-menu"
:class="[
{ show: isOpen },
{ 'dropdown-menu-right': menuOnRight },
menuClasses
]"
:class="[{ show: isOpen }, { 'dropdown-menu-right': menuOnRight }, menuClasses]"
>
<slot></slot>
</ul>
@ -37,60 +34,60 @@ export default {
tag: {
type: String,
default: 'div',
description: 'Dropdown html tag (e.g div, ul etc)'
description: 'Dropdown html tag (e.g div, ul etc)',
},
titleTag: {
type: String,
default: 'button',
description: 'Dropdown title (toggle) html tag'
description: 'Dropdown title (toggle) html tag',
},
title: {
type: String,
description: 'Dropdown title'
description: 'Dropdown title',
},
direction: {
type: String,
default: 'down', // up | down
description: 'Dropdown menu direction (up|down)'
description: 'Dropdown menu direction (up|down)',
},
icon: {
type: String,
description: 'Dropdown icon'
description: 'Dropdown icon',
},
titleClasses: {
type: [String, Object, Array],
description: 'Title css classes'
description: 'Title css classes',
},
menuClasses: {
type: [String, Object],
description: 'Menu css classes'
description: 'Menu css classes',
},
menuOnRight: {
type: Boolean,
description: 'Whether menu should appear on the right'
description: 'Whether menu should appear on the right',
},
hasToggle: {
type: Boolean,
description: 'Whether dropdown has arrow icon shown',
default: true
}
default: true,
},
},
data() {
return {
isOpen: false
};
isOpen: false,
}
},
methods: {
toggleDropDown() {
this.isOpen = !this.isOpen;
this.$emit('change', this.isOpen);
this.isOpen = !this.isOpen
this.$emit('change', this.isOpen)
},
closeDropDown() {
this.isOpen = false;
this.$emit('change', false);
}
}
};
this.isOpen = false
this.$emit('change', false)
},
},
}
</script>
<style lang="scss" scoped>
.dropdown {

View File

@ -1,23 +1,22 @@
<template>
<div class="header" :class="{[`bg-${type}`]: type}">
<b-container fluid>
<div class="header" :class="{ [`bg-${type}`]: type }">
<b-container fluid>
<div class="header-body">
<slot></slot>
</div>
</b-container>
</b-container>
</div>
</template>
<script>
export default {
name: 'base-header',
props: {
type: {
type: String,
default: 'success',
description: 'Header background type'
}
}
}
export default {
name: 'base-header',
props: {
type: {
type: String,
default: 'success',
description: 'Header background type',
},
},
}
</script>
<style>
</style>
<style></style>

View File

@ -1,14 +1,27 @@
<template>
<div>
<b-pagination first-number last-number :per-page="perPage" :size="size" :value="value" @change="(val) => $emit('change', val)" :align="align" :total-rows="total">
<b-pagination
first-number
last-number
:per-page="perPage"
:size="size"
:value="value"
@change="(val) => $emit('change', val)"
:align="align"
:total-rows="total"
>
<template v-slot:prev-text>
<a class="page-link" aria-label="Previous">
<span aria-hidden="true"><i class="fa fa-angle-left" aria-hidden="true"></i></span>
<span aria-hidden="true">
<i class="fa fa-angle-left" aria-hidden="true"></i>
</span>
</a>
</template>
<template v-slot:next-text>
<a class="page-link" aria-label="Next">
<span aria-hidden="true"><i class="fa fa-angle-right" aria-hidden="true"></i></span>
<span aria-hidden="true">
<i class="fa fa-angle-right" aria-hidden="true"></i>
</span>
</a>
</template>
</b-pagination>
@ -16,41 +29,39 @@
</template>
<script>
export default {
name: "base-pagination",
name: 'base-pagination',
props: {
pageCount: {
type: Number,
default: 0,
description:
"Pagination page count. This should be specified in combination with perPage"
description: 'Pagination page count. This should be specified in combination with perPage',
},
perPage: {
type: Number,
default: 10,
description:
"Pagination per page. Should be specified with total or pageCount"
description: 'Pagination per page. Should be specified with total or pageCount',
},
total: {
type: Number,
default: 0,
description:
"Can be specified instead of pageCount. The page count in this case will be total/perPage"
'Can be specified instead of pageCount. The page count in this case will be total/perPage',
},
value: {
type: Number,
default: 1,
description: "Pagination value"
description: 'Pagination value',
},
size: {
type: String,
default: "",
description: "Pagination size"
default: '',
description: 'Pagination size',
},
align: {
type: String,
default: "",
description: "Pagination alignment (e.g center|start|end)"
}
default: '',
description: 'Pagination alignment (e.g center|start|end)',
},
},
};
}
</script>

View File

@ -3,82 +3,77 @@
<div :class="`progress-${type}`" v-if="showLabel">
<div class="progress-label">
<slot name="label">
<span>{{label}}</span>
<span>{{ label }}</span>
</slot>
</div>
<div class="progress-percentage">
<slot>
<span>{{value}}%</span>
<span>{{ value }}%</span>
</slot>
</div>
</div>
<b-progress
:size="size"
:class="[progressClasses]"
:style="`height: ${height}px`">
<b-progress :size="size" :class="[progressClasses]" :style="`height: ${height}px`">
<b-progress-bar :class="computedClasses" :value="value"></b-progress-bar>
</b-progress>
</div>
</template>
<script>
export default {
name: "base-progress",
props: {
striped: {
type: Boolean,
description: "Whether progress is striped"
},
animated: {
type: Boolean,
description:
"Whether progress is animated (works only with `striped` prop together)"
},
label: {
type: String,
description: "Progress label (shown on the left above progress)"
},
height: {
type: Number,
default: 3,
description: "Progress line height"
},
type: {
type: String,
default: "default",
description: "Progress type (e.g danger, primary etc)"
},
showLabel: {
type: Boolean,
default: false
},
progressClasses: {
type: [Array, String],
default: '',
description: 'Progress css classes'
},
size: {
type: String,
default: ''
},
value: {
type: Number,
default: 0,
validator: value => {
return value >= 0 && value <= 100;
},
description: "Progress value"
}
export default {
name: 'base-progress',
props: {
striped: {
type: Boolean,
description: 'Whether progress is striped',
},
computed: {
computedClasses() {
return [
{ "progress-bar-striped": this.striped },
{ "progress-bar-animated": this.animated },
{ [`bg-${this.type}`]: this.type }
];
}
}
};
animated: {
type: Boolean,
description: 'Whether progress is animated (works only with `striped` prop together)',
},
label: {
type: String,
description: 'Progress label (shown on the left above progress)',
},
height: {
type: Number,
default: 3,
description: 'Progress line height',
},
type: {
type: String,
default: 'default',
description: 'Progress type (e.g danger, primary etc)',
},
showLabel: {
type: Boolean,
default: false,
},
progressClasses: {
type: [Array, String],
default: '',
description: 'Progress css classes',
},
size: {
type: String,
default: '',
},
value: {
type: Number,
default: 0,
validator: (value) => {
return value >= 0 && value <= 100
},
description: 'Progress value',
},
},
computed: {
computedClasses() {
return [
{ 'progress-bar-striped': this.striped },
{ 'progress-bar-animated': this.animated },
{ [`bg-${this.type}`]: this.type },
]
},
},
}
</script>
<style>
</style>
<style></style>

View File

@ -2,56 +2,55 @@
<div class="slider" :disabled="disabled"></div>
</template>
<script>
import noUiSlider from 'nouislider';
import noUiSlider from 'nouislider'
export default {
name: 'base-slider',
props: {
value: {
type: [String, Array, Number],
description: 'slider value'
description: 'slider value',
},
disabled: {
type: Boolean,
default: false,
description: 'whether the slider is disabled'
description: 'whether the slider is disabled',
},
start: {
type: [Number, Array],
default: 0,
description:
'[noUi Slider start](https://refreshless.com/nouislider/slider-options/#section-start)'
'[noUi Slider start](https://refreshless.com/nouislider/slider-options/#section-start)',
},
connect: {
type: [Boolean, Array],
default: () => [true, false],
description:
'[noUi Slider connect](https://refreshless.com/nouislider/slider-options/#section-connect)'
'[noUi Slider connect](https://refreshless.com/nouislider/slider-options/#section-connect)',
},
range: {
type: Object,
default: () => {
return {
min: 0,
max: 100
};
max: 100,
}
},
description:
'[noUi Slider range](https://refreshless.com/nouislider/slider-values/#section-range)'
'[noUi Slider range](https://refreshless.com/nouislider/slider-values/#section-range)',
},
options: {
type: Object,
default: () => {
return {};
return {}
},
description:
'[noUi Slider options](https://refreshless.com/nouislider/slider-options/)'
}
description: '[noUi Slider options](https://refreshless.com/nouislider/slider-options/)',
},
},
data() {
return {
slider: null
};
slider: null,
}
},
methods: {
createSlider() {
@ -59,38 +58,35 @@ export default {
start: this.value || this.start,
connect: Array.isArray(this.value) ? true : this.connect,
range: this.range,
...this.options
});
const slider = this.$el.noUiSlider;
...this.options,
})
const slider = this.$el.noUiSlider
slider.on('slide', () => {
let value = slider.get();
let value = slider.get()
if (value !== this.value) {
this.$emit('input', value);
this.$emit('input', value)
}
});
}
})
},
},
mounted() {
this.createSlider();
this.createSlider()
},
watch: {
value(newValue, oldValue) {
const slider = this.$el.noUiSlider;
const sliderValue = slider.get();
const slider = this.$el.noUiSlider
const sliderValue = slider.get()
if (newValue !== oldValue && sliderValue !== newValue) {
if (Array.isArray(sliderValue) && Array.isArray(newValue)) {
if (
oldValue.length === newValue.length &&
oldValue.every((v, i) => v === newValue[i])
) {
slider.set(newValue);
if (oldValue.length === newValue.length && oldValue.every((v, i) => v === newValue[i])) {
slider.set(newValue)
}
} else {
slider.set(newValue);
slider.set(newValue)
}
}
}
}
};
},
},
}
</script>
<style></style>

View File

@ -10,10 +10,7 @@
<tbody :class="tbodyClasses">
<tr v-for="(item, index) in data" :key="index">
<slot :row="item" :index="index">
<td
v-for="(column, index) in columns"
:key="index"
>
<td v-for="(column, index) in columns" :key="index">
{{ itemValue(item, column) }}
</td>
</slot>
@ -28,42 +25,42 @@ export default {
columns: {
type: Array,
default: () => [],
description: 'Table columns'
description: 'Table columns',
},
data: {
type: Array,
default: () => [],
description: 'Table data'
description: 'Table data',
},
type: {
type: String, // striped | hover
default: '',
description: 'Whether table is striped or hover type'
description: 'Whether table is striped or hover type',
},
theadClasses: {
type: String,
default: '',
description: '<thead> css classes'
description: '<thead> css classes',
},
tbodyClasses: {
type: String,
default: '',
description: '<tbody> css classes'
}
description: '<tbody> css classes',
},
},
computed: {
tableClass() {
return this.type && `table-${this.type}`;
}
return this.type && `table-${this.type}`
},
},
methods: {
hasValue(item, column) {
return item[column.toLowerCase()] !== 'undefined';
return item[column.toLowerCase()] !== 'undefined'
},
itemValue(item, column) {
return item[column.toLowerCase()];
}
}
};
return item[column.toLowerCase()]
},
},
}
</script>
<style></style>

View File

@ -1,7 +1,6 @@
<template>
<nav aria-label="breadcrumb">
<b-breadcrumb
:class="[{[`bg-${type}`]: type}, listClasses]">
<b-breadcrumb :class="[{ [`bg-${type}`]: type }, listClasses]">
<slot></slot>
</b-breadcrumb>
</nav>
@ -13,14 +12,14 @@ export default {
type: {
type: String,
default: '',
description: 'Breadcrumb background type'
description: 'Breadcrumb background type',
},
listClasses: {
type: [String, Object],
default: '',
description: 'Breadcrumb list classes'
}
}
};
description: 'Breadcrumb list classes',
},
},
}
</script>
<style></style>

View File

@ -10,9 +10,9 @@ export default {
active: {
type: Boolean,
default: false,
description: 'Whether breadcrumb item is active'
}
}
};
description: 'Whether breadcrumb item is active',
},
},
}
</script>
<style></style>

View File

@ -1,43 +1,40 @@
<template>
<bread-crumb list-classes="breadcrumb-links breadcrumb-dark">
<BreadCrumbItem>
<router-link to="/">
<i class="fas fa-home"></i>
</router-link>
</BreadCrumbItem>
<BreadCrumbItem
<bread-crumb-item>
<router-link to="/overview">
<i class="fas fa-home"></i>
</router-link>
</bread-crumb-item>
<bread-crumb-item
v-for="(route, index) in $route.matched.slice()"
:key="route.name"
:active="index === $route.matched.length - 1"
style="display:inline-block"
style="display: inline-block"
>
<router-link
:to="{ name: route.name }"
v-if="index < $route.matched.length - 1"
>
<router-link :to="{ name: route.name }" v-if="index < $route.matched.length - 1">
{{ route.name }}
</router-link>
<span v-else>{{ route.name }}</span>
</BreadCrumbItem>
</bread-crumb-item>
</bread-crumb>
</template>
<script>
import BreadCrumb from './Breadcrumb';
import BreadCrumbItem from './BreadcrumbItem';
import BreadCrumb from './Breadcrumb'
import BreadCrumbItem from './BreadcrumbItem'
export default {
name: 'route-breadcrumb',
components: {
BreadCrumb,
BreadCrumbItem
export default {
name: 'route-breadcrumb',
components: {
BreadCrumb,
BreadCrumbItem,
},
methods: {
getBreadName(route) {
return route.name
},
methods: {
getBreadName(route) {
return route.name;
}
}
};
},
}
</script>
<style scoped></style>

View File

@ -1,39 +1,38 @@
<template>
<div class="btn-group-toggle" data-toggle="buttons">
<label class="btn" :class="[{ active: value }, buttonClasses]">
<input v-model="model" type="checkbox" checked="" autocomplete="off">
<input v-model="model" type="checkbox" checked="" autocomplete="off" />
<slot></slot>
</label>
</div>
</template>
<script>
export default {
name: 'button-checkbox',
props: {
value: {
type: Boolean,
description: 'Checked value'
},
buttonClasses: {
type: [String, Object],
description: 'Inner button css classes'
}
export default {
name: 'button-checkbox',
props: {
value: {
type: Boolean,
description: 'Checked value',
},
buttonClasses: {
type: [String, Object],
description: 'Inner button css classes',
},
},
model: {
prop: 'value',
event: 'change',
},
computed: {
model: {
prop: 'value',
event: 'change'
get() {
return this.value
},
set(val) {
this.$emit('change', val)
},
},
computed: {
model: {
get() {
return this.value
},
set(val) {
this.$emit('change', val)
}
}
}
}
},
}
</script>
<style>
</style>
<style></style>

View File

@ -1,47 +1,55 @@
<template>
<div class="btn-group-toggle" data-toggle="buttons">
<label v-for="(option, index) in options"
:key="index"
class="btn"
:class="[{ active: value === option.value }, buttonClasses]">
<input :value="option.value" v-model="model" type="radio" id="option1" autocomplete="off" checked="">
{{option.label}}
<label
v-for="(option, index) in options"
:key="index"
class="btn"
:class="[{ active: value === option.value }, buttonClasses]"
>
<input
:value="option.value"
v-model="model"
type="radio"
id="option1"
autocomplete="off"
checked=""
/>
{{ option.label }}
</label>
</div>
</template>
<script>
export default {
name: 'button-radio-group',
props: {
options: {
type: Array,
description: 'Radio options. Should be an array of objects {value: "", label: ""}',
default: () => []
},
value: {
type: String,
description: 'Radio value'
},
buttonClasses: {
type: [String, Object],
description: 'Inner button css classes'
}
export default {
name: 'button-radio-group',
props: {
options: {
type: Array,
description: 'Radio options. Should be an array of objects {value: "", label: ""}',
default: () => [],
},
value: {
type: String,
description: 'Radio value',
},
buttonClasses: {
type: [String, Object],
description: 'Inner button css classes',
},
},
model: {
prop: 'value',
event: 'change',
},
computed: {
model: {
prop: 'value',
event: 'change'
get() {
return this.value
},
set(val) {
this.$emit('change', val)
},
},
computed: {
model: {
get() {
return this.value
},
set(val) {
this.$emit('change', val)
}
}
}
}
},
}
</script>
<style>
</style>
<style></style>

View File

@ -1,18 +1,17 @@
<template>
<b-card
no-body
:class="[
{'card-lift--hover': hover},
{'shadow': shadow},
{[`shadow-${shadowSize}`]: shadowSize},
{[`bg-gradient-${gradient}`]: gradient},
{[`bg-${type}`]: type}
]">
no-body
:class="[
{ 'card-lift--hover': hover },
{ shadow: shadow },
{ [`shadow-${shadowSize}`]: shadowSize },
{ [`bg-gradient-${gradient}`]: gradient },
{ [`bg-${type}`]: type },
]"
>
<slot name="image"></slot>
<b-card-header :class="headerClasses" v-if="$slots.header">
<slot name="header">
</slot>
<slot name="header"></slot>
</b-card-header>
<b-card-body :class="bodyClasses" v-if="!noBody">
<slot></slot>
@ -26,48 +25,47 @@
</b-card>
</template>
<script>
export default {
name: "card",
props: {
type: {
type: String,
description: "Card type"
},
gradient: {
type: String,
description: "Card background gradient type (warning,danger etc)"
},
hover: {
type: Boolean,
description: "Whether card should move on hover"
},
shadow: {
type: Boolean,
description: "Whether card has shadow"
},
shadowSize: {
type: String,
description: "Card shadow size"
},
noBody: {
type: Boolean,
default: false,
description: "Whether card should have wrapper body class"
},
bodyClasses: {
type: [String, Object, Array],
description: "Card body css classes"
},
headerClasses: {
type: [String, Object, Array],
description: "Card header css classes"
},
footerClasses: {
type: [String, Object, Array],
description: "Card footer css classes"
}
}
};
export default {
name: 'card',
props: {
type: {
type: String,
description: 'Card type',
},
gradient: {
type: String,
description: 'Card background gradient type (warning,danger etc)',
},
hover: {
type: Boolean,
description: 'Whether card should move on hover',
},
shadow: {
type: Boolean,
description: 'Whether card has shadow',
},
shadowSize: {
type: String,
description: 'Card shadow size',
},
noBody: {
type: Boolean,
default: false,
description: 'Whether card should have wrapper body class',
},
bodyClasses: {
type: [String, Object, Array],
description: 'Card body css classes',
},
headerClasses: {
type: [String, Object, Array],
description: 'Card header css classes',
},
footerClasses: {
type: [String, Object, Array],
description: 'Card footer css classes',
},
},
}
</script>
<style>
</style>
<style></style>

View File

@ -1,57 +1,58 @@
<template>
<card class="card-stats" :show-footer-line="true">
<b-row>
<b-col>
<slot>
<h5 class="card-title text-uppercase text-muted mb-0" v-if="title">{{title}}</h5>
<span class="h2 font-weight-bold mb-0" v-if="subTitle">{{subTitle}}</span>
<h5 class="card-title text-uppercase text-muted mb-0" v-if="title">
{{ title }}
</h5>
<span class="h2 font-weight-bold mb-0" v-if="subTitle">
{{ subTitle }}
</span>
</slot>
</b-col>
<b-col cols="auto" v-if="$slots.icon || icon">
<slot name="icon">
<div class="icon icon-shape text-white rounded-circle shadow"
:class="[`bg-${type}`, iconClasses]">
<div
class="icon icon-shape text-white rounded-circle shadow"
:class="[`bg-${type}`, iconClasses]"
>
<i :class="icon"></i>
</div>
</slot>
</b-col>
<b-col cols="auto" v-if="$slots.img || img">
<b-col cols="auto" v-if="$slots.img || img">
<slot name="img">
<img :src="img" width="80" />
</slot>
</b-col>
</b-row>
<p class="mt-3 mb-0 text-sm">
<slot name="footer">
</slot>
<slot name="footer"></slot>
</p>
</card>
</template>
<script>
import Card from './Card.vue';
import Card from './Card.vue'
export default {
name: 'stats-card',
components: {
Card
export default {
name: 'stats-card',
components: {
Card,
},
props: {
type: {
type: String,
default: 'primary',
},
props: {
type: {
type: String,
default: 'primary'
},
icon: String,
img: String,
title: String,
subTitle: String,
iconClasses: [String, Array]
}
};
icon: String,
img: String,
title: String,
subTitle: String,
iconClasses: [String, Array],
},
}
</script>
<style></style>

View File

@ -1,5 +1,5 @@
import { Bar, mixins } from 'vue-chartjs';
import globalOptionsMixin from "@/components/Charts/globalOptionsMixin";
import { Bar, mixins } from 'vue-chartjs'
import globalOptionsMixin from '@/components/Charts/globalOptionsMixin'
export default {
name: 'bar-chart',
@ -8,23 +8,23 @@ export default {
props: {
extraOptions: {
type: Object,
default: () => ({})
}
default: () => ({}),
},
},
data() {
return {
ctx: null
};
ctx: null,
}
},
mounted() {
this.$watch(
'chartData',
(newVal, oldVal) => {
if (!oldVal) {
this.renderChart(this.chartData, this.extraOptions);
this.renderChart(this.chartData, this.extraOptions)
}
},
{ immediate: true }
);
}
};
{ immediate: true },
)
},
}

View File

@ -1,5 +1,5 @@
import { Line, mixins } from 'vue-chartjs';
import globalOptionsMixin from "@/components/Charts/globalOptionsMixin";
import { Line, mixins } from 'vue-chartjs'
import globalOptionsMixin from '@/components/Charts/globalOptionsMixin'
export default {
name: 'line-chart',
extends: Line,
@ -7,23 +7,23 @@ export default {
props: {
extraOptions: {
type: Object,
default: () => ({})
}
default: () => ({}),
},
},
data() {
return {
ctx: null
};
ctx: null,
}
},
mounted() {
this.$watch(
'chartData',
(newVal, oldVal) => {
if (!oldVal) {
this.renderChart(this.chartData, this.extraOptions);
this.renderChart(this.chartData, this.extraOptions)
}
},
{ immediate: true }
);
}
};
{ immediate: true },
)
},
}

View File

@ -1,10 +1,10 @@
import { parseOptions } from "@/components/Charts/optionHelpers";
import { parseOptions } from '@/components/Charts/optionHelpers'
import Chart from 'chart.js'
export const Charts = {
mode: 'light',//(themeMode) ? themeMode : 'light';
mode: 'light', //(themeMode) ? themeMode : 'light';
fonts: {
base: 'Open Sans'
base: 'Open Sans',
},
colors: {
gray: {
@ -16,114 +16,115 @@ export const Charts = {
600: '#8898aa',
700: '#525f7f',
800: '#32325d',
900: '#212529'
900: '#212529',
},
theme: {
'default': '#172b4d',
'primary': '#5e72e4',
'secondary': '#f4f5f7',
'info': '#11cdef',
'success': '#2dce89',
'danger': '#f5365c',
'warning': '#fb6340'
default: '#172b4d',
primary: '#5e72e4',
secondary: '#f4f5f7',
info: '#11cdef',
success: '#2dce89',
danger: '#f5365c',
warning: '#fb6340',
},
black: '#12263F',
white: '#FFFFFF',
transparent: 'transparent',
}
};
},
}
function chartOptions() {
let { colors, mode, fonts } = Charts;
let { colors, mode, fonts } = Charts
// Options
let options = {
defaults: {
global: {
responsive: true,
maintainAspectRatio: false,
defaultColor: (mode === 'dark') ? colors.gray[700] : colors.gray[600],
defaultFontColor: (mode === 'dark') ? colors.gray[700] : colors.gray[600],
defaultColor: mode === 'dark' ? colors.gray[700] : colors.gray[600],
defaultFontColor: mode === 'dark' ? colors.gray[700] : colors.gray[600],
defaultFontFamily: fonts.base,
defaultFontSize: 13,
layout: {
padding: 0
padding: 0,
},
legend: {
display: false,
position: 'bottom',
labels: {
usePointStyle: true,
padding: 16
}
padding: 16,
},
},
elements: {
point: {
radius: 0,
backgroundColor: colors.theme['primary']
backgroundColor: colors.theme['primary'],
},
line: {
tension: .4,
tension: 0.4,
borderWidth: 4,
borderColor: colors.theme['primary'],
backgroundColor: colors.transparent,
borderCapStyle: 'rounded'
borderCapStyle: 'rounded',
},
rectangle: {
backgroundColor: colors.theme['warning']
backgroundColor: colors.theme['warning'],
},
arc: {
backgroundColor: colors.theme['primary'],
borderColor: (mode == 'dark') ? colors.gray[800] : colors.white,
borderWidth: 4
}
borderColor: mode == 'dark' ? colors.gray[800] : colors.white,
borderWidth: 4,
},
},
tooltips: {
enabled: true,
mode: 'index',
intersect: false,
}
},
},
pie: {
tooltips: {
mode: 'point'
}
mode: 'point',
},
},
doughnut: {
tooltips: {
mode: 'point'
mode: 'point',
},
cutoutPercentage: 83,
legendCallback: function (chart) {
let data = chart.data;
let content = '';
let data = chart.data
let content = ''
data.labels.forEach(function (label, index) {
let bgColor = data.datasets[0].backgroundColor[index];
let bgColor = data.datasets[0].backgroundColor[index]
content += '<span class="chart-legend-item">';
content += '<i class="chart-legend-indicator" style="background-color: ' + bgColor + '"></i>';
content += label;
content += '</span>';
});
content += '<span class="chart-legend-item">'
content +=
'<i class="chart-legend-indicator" style="background-color: ' + bgColor + '"></i>'
content += label
content += '</span>'
})
return content;
}
}
}
};
return content
},
},
},
}
// yAxes
Chart.scaleService.updateScaleDefaults('linear', {
gridLines: {
borderDash: [2],
borderDashOffset: [2],
color: (mode === 'dark') ? colors.gray[900] : colors.gray[200],
color: mode === 'dark' ? colors.gray[900] : colors.gray[200],
drawBorder: false,
drawTicks: true,
zeroLineWidth: 1,
zeroLineColor: (mode === 'dark') ? colors.gray[900] : colors.gray[200],
zeroLineColor: mode === 'dark' ? colors.gray[900] : colors.gray[200],
zeroLineBorderDash: [2],
zeroLineBorderDashOffset: [2]
zeroLineBorderDashOffset: [2],
},
ticks: {
beginAtZero: true,
@ -132,9 +133,9 @@ function chartOptions() {
if (!(value % 10)) {
return value
}
}
}
});
},
},
})
// xAxes
Chart.scaleService.updateScaleDefaults('category', {
@ -146,13 +147,13 @@ function chartOptions() {
zeroLineWidth: 1,
},
ticks: {
padding: 20
padding: 20,
},
maxBarThickness: 10
});
maxBarThickness: 10,
})
return options;
};
return options
}
let initialized = false
@ -160,27 +161,29 @@ export function initGlobalOptions() {
if (initialized) {
return
}
parseOptions(Chart, chartOptions());
parseOptions(Chart, chartOptions())
initialized = true
}
export const basicOptions = {
maintainAspectRatio: false,
legend: {
display: false
display: false,
},
responsive: true
};
responsive: true,
}
export let blueChartOptions = {
scales: {
yAxes: [{
gridLines: {
color: Charts.colors.gray[700],
zeroLineColor: Charts.colors.gray[700]
}
}]
}
};
yAxes: [
{
gridLines: {
color: Charts.colors.gray[700],
zeroLineColor: Charts.colors.gray[700],
},
},
],
},
}
export let lineChartOptionsBlue = {
...basicOptions,
@ -192,7 +195,7 @@ export let lineChartOptionsBlue = {
xPadding: 12,
mode: 'nearest',
intersect: 0,
position: 'nearest'
position: 'nearest',
},
responsive: true,
scales: {
@ -202,15 +205,15 @@ export let lineChartOptionsBlue = {
gridLines: {
drawBorder: false,
color: 'rgba(29,140,248,0.0)',
zeroLineColor: 'transparent'
zeroLineColor: 'transparent',
},
ticks: {
suggestedMin: 60,
suggestedMax: 125,
padding: 20,
fontColor: '#9e9e9e'
}
}
fontColor: '#9e9e9e',
},
},
],
xAxes: [
@ -219,13 +222,13 @@ export let lineChartOptionsBlue = {
gridLines: {
drawBorder: false,
color: 'rgba(29,140,248,0.1)',
zeroLineColor: 'transparent'
zeroLineColor: 'transparent',
},
ticks: {
padding: 20,
fontColor: '#9e9e9e'
}
}
]
}
};
fontColor: '#9e9e9e',
},
},
],
},
}

View File

@ -1,7 +1,7 @@
import { initGlobalOptions } from "@/components/Charts/config";
import { initGlobalOptions } from '@/components/Charts/config'
import './roundedCornersExtension'
export default {
mounted() {
initGlobalOptions();
}
initGlobalOptions()
},
}

View File

@ -2,9 +2,9 @@
export function parseOptions(parent, options) {
for (let item in options) {
if (typeof options[item] !== 'object') {
parent[item] = options[item];
parent[item] = options[item]
} else {
parseOptions(parent[item], options[item]);
parseOptions(parent[item], options[item])
}
}
}

View File

@ -3,64 +3,63 @@
// Code from: https://codepen.io/jedtrow/full/ygRYgo
//
import Chart from 'chart.js'
Chart.elements.Rectangle.prototype.draw = function() {
let ctx = this._chart.ctx;
let vm = this._view;
let left, right, top, bottom, signX, signY, borderSkipped, radius;
let borderWidth = vm.borderWidth;
Chart.elements.Rectangle.prototype.draw = function () {
let ctx = this._chart.ctx
let vm = this._view
let left, right, top, bottom, signX, signY, borderSkipped, radius
let borderWidth = vm.borderWidth
// Set Radius Here
// If radius is large enough to cause drawing errors a max radius is imposed
let cornerRadius = 6;
let cornerRadius = 6
if (!vm.horizontal) {
// bar
left = vm.x - vm.width / 2;
right = vm.x + vm.width / 2;
top = vm.y;
bottom = vm.base;
signX = 1;
signY = bottom > top ? 1 : -1;
borderSkipped = vm.borderSkipped || 'bottom';
left = vm.x - vm.width / 2
right = vm.x + vm.width / 2
top = vm.y
bottom = vm.base
signX = 1
signY = bottom > top ? 1 : -1
borderSkipped = vm.borderSkipped || 'bottom'
} else {
// horizontal bar
left = vm.base;
right = vm.x;
top = vm.y - vm.height / 2;
bottom = vm.y + vm.height / 2;
signX = right > left ? 1 : -1;
signY = 1;
borderSkipped = vm.borderSkipped || 'left';
left = vm.base
right = vm.x
top = vm.y - vm.height / 2
bottom = vm.y + vm.height / 2
signX = right > left ? 1 : -1
signY = 1
borderSkipped = vm.borderSkipped || 'left'
}
// Canvas doesn't allow us to stroke inside the width so we can
// adjust the sizes to fit if we're setting a stroke on the line
if (borderWidth) {
// borderWidth shold be less than bar width and bar height.
let barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
borderWidth = borderWidth > barSize ? barSize : borderWidth;
let halfStroke = borderWidth / 2;
let barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom))
borderWidth = borderWidth > barSize ? barSize : borderWidth
let halfStroke = borderWidth / 2
// Adjust borderWidth when bar top position is near vm.base(zero).
let borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
let borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
let borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
let borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
let borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0)
let borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0)
let borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0)
let borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0)
// not become a vertical line?
if (borderLeft !== borderRight) {
top = borderTop;
bottom = borderBottom;
top = borderTop
bottom = borderBottom
}
// not become a horizontal line?
if (borderTop !== borderBottom) {
left = borderLeft;
right = borderRight;
left = borderLeft
right = borderRight
}
}
ctx.beginPath();
ctx.fillStyle = vm.backgroundColor;
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = borderWidth;
ctx.beginPath()
ctx.fillStyle = vm.backgroundColor
ctx.strokeStyle = vm.borderColor
ctx.lineWidth = borderWidth
// Corner points, from bottom-left to bottom-right clockwise
// | 1 2 |
@ -69,62 +68,61 @@ Chart.elements.Rectangle.prototype.draw = function() {
[left, bottom],
[left, top],
[right, top],
[right, bottom]
];
[right, bottom],
]
// Find first (starting) corner with fallback to 'bottom'
let borders = ['bottom', 'left', 'top', 'right'];
let startCorner = borders.indexOf(borderSkipped, 0);
let borders = ['bottom', 'left', 'top', 'right']
let startCorner = borders.indexOf(borderSkipped, 0)
if (startCorner === -1) {
startCorner = 0;
startCorner = 0
}
function cornerAt(index) {
return corners[(startCorner + index) % 4];
return corners[(startCorner + index) % 4]
}
// Draw rectangle from 'startCorner'
let corner = cornerAt(0);
ctx.moveTo(corner[0], corner[1]);
let corner = cornerAt(0)
ctx.moveTo(corner[0], corner[1])
for (let i = 1; i < 4; i++) {
corner = cornerAt(i);
let nextCornerId = i + 1;
corner = cornerAt(i)
let nextCornerId = i + 1
if (nextCornerId == 4) {
nextCornerId = 0
}
let nextCorner = cornerAt(nextCornerId);
let nextCorner = cornerAt(nextCornerId)
let width = corners[2][0] - corners[1][0];
let height = corners[0][1] - corners[1][1];
let x = corners[1][0];
let y = corners[1][1];
let width = corners[2][0] - corners[1][0]
let height = corners[0][1] - corners[1][1]
let x = corners[1][0]
let y = corners[1][1]
let radius = cornerRadius;
let radius = cornerRadius
// Fix radius being too large
if (radius > height / 2) {
radius = height / 2;
radius = height / 2
}
if (radius > width / 2) {
radius = width / 2;
radius = width / 2
}
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.moveTo(x + radius, y)
ctx.lineTo(x + width - radius, y)
ctx.quadraticCurveTo(x + width, y, x + width, y + radius)
ctx.lineTo(x + width, y + height - radius)
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height)
ctx.lineTo(x + radius, y + height)
ctx.quadraticCurveTo(x, y + height, x, y + height - radius)
ctx.lineTo(x, y + radius)
ctx.quadraticCurveTo(x, y, x + radius, y)
}
ctx.fill();
ctx.fill()
if (borderWidth) {
ctx.stroke();
ctx.stroke()
}
};
}

View File

@ -0,0 +1,28 @@
import { mount } from '@vue/test-utils'
import CloseButton from './CloseButton'
const localVue = global.localVue
describe('CloseButton', () => {
let wrapper
let propsData = {
target: 'Target',
expanded: false,
}
const Wrapper = () => {
return mount(CloseButton, { localVue, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('emmits click event', () => {
wrapper.find('.navbar-toggler').trigger('click')
expect(wrapper.emitted('click')).toBeTruthy()
})
})
})

View File

@ -9,7 +9,8 @@
:aria-expanded="expanded"
aria-label="Toggle navigation"
>
<span></span> <span></span>
<span></span>
<span></span>
</button>
</template>
<script>
@ -18,18 +19,18 @@ export default {
props: {
target: {
type: [String, Number],
description: 'Close button target element'
description: 'Close button target element',
},
expanded: {
type: Boolean,
description: 'Whether button is expanded (aria-expanded attribute)'
}
description: 'Whether button is expanded (aria-expanded attribute)',
},
},
methods: {
handleClick(evt) {
this.$emit('click', evt);
}
}
};
this.$emit('click', evt)
},
},
}
</script>
<style></style>

View File

@ -1,10 +1,5 @@
<template>
<div
id="accordion"
role="tablist"
aria-multiselectable="true"
class="accordion"
>
<div id="accordion" role="tablist" aria-multiselectable="true" class="accordion">
<slot></slot>
</div>
</template>
@ -16,18 +11,18 @@ export default {
animationDuration: {
type: Number,
default: 250,
description: 'Collapse animation duration'
description: 'Collapse animation duration',
},
multipleActive: {
type: Boolean,
default: true,
description: 'Whether you can have multiple collapse items opened at the same time'
description: 'Whether you can have multiple collapse items opened at the same time',
},
activeIndex: {
type: Number,
default: -1,
description: 'Active collapse item index'
}
description: 'Active collapse item index',
},
},
provide() {
return {
@ -35,50 +30,50 @@ export default {
multipleActive: this.multipleActive,
addItem: this.addItem,
removeItem: this.removeItem,
deactivateAll: this.deactivateAll
};
deactivateAll: this.deactivateAll,
}
},
data() {
return {
items: []
};
items: [],
}
},
methods: {
addItem(item) {
const index = this.$slots.default.indexOf(item.$vnode);
const index = this.$slots.default.indexOf(item.$vnode)
if (index !== -1) {
this.items.splice(index, 0, item);
this.items.splice(index, 0, item)
}
},
removeItem(item) {
const items = this.items;
const index = items.indexOf(item);
const items = this.items
const index = items.indexOf(item)
if (index > -1) {
items.splice(index, 1);
items.splice(index, 1)
}
},
deactivateAll() {
this.items.forEach(item => {
item.active = false;
});
this.items.forEach((item) => {
item.active = false
})
},
activateItem() {
if (this.activeIndex !== -1) {
this.items[this.activeIndex].active = true;
this.items[this.activeIndex].active = true
}
}
},
},
mounted() {
this.$nextTick(() => {
this.activateItem();
});
this.activateItem()
})
},
watch: {
activeIndex() {
this.activateItem();
}
}
};
this.activateItem()
},
},
}
</script>
<style scoped></style>

View File

@ -8,7 +8,7 @@
@click.prevent="activate"
:aria-controls="`content-${itemId}`"
>
<slot name="title"> {{ title }} </slot>
<slot name="title">{{ title }}</slot>
<i class="tim-icons icon-minimal-down"></i>
</a>
</b-card-header>
@ -26,67 +26,66 @@
</b-card>
</template>
<script>
import { CollapseTransition } from 'vue2-transitions';
import { CollapseTransition } from 'vue2-transitions'
export default {
name: 'collapse-item',
components: {
CollapseTransition
CollapseTransition,
},
props: {
title: {
type: String,
default: '',
description: 'Collapse item title'
description: 'Collapse item title',
},
id: String
id: String,
},
inject: {
animationDuration: {
default: 250
default: 250,
},
multipleActive: {
default: false
default: false,
},
addItem: {
default: () => {}
default: () => {},
},
removeItem: {
default: () => {}
default: () => {},
},
deactivateAll: {
default: () => {}
}
default: () => {},
},
},
computed: {
itemId() {
return this.id || this.title;
}
return this.id || this.title
},
},
data() {
return {
active: false
};
active: false,
}
},
methods: {
activate() {
let wasActive = this.active;
let wasActive = this.active
if (!this.multipleActive) {
this.deactivateAll();
this.deactivateAll()
}
this.active = !wasActive;
console.log(this.active)
}
this.active = !wasActive
},
},
mounted() {
this.addItem(this);
this.addItem(this)
},
destroyed() {
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el);
this.$el.parentNode.removeChild(this.$el)
}
this.removeItem(this);
}
};
this.removeItem(this)
},
}
</script>
<style></style>

View File

@ -1,14 +1,16 @@
<template>
<div class="custom-control custom-checkbox"
:class="[
{disabled: disabled},
{[`custom-checkbox-${type}`]: type},inlineClass]">
<input :id="cbId"
class="custom-control-input"
:class="inputClasses"
type="checkbox"
:disabled="disabled"
v-model="model"/>
<div
class="custom-control custom-checkbox"
:class="[{ disabled: disabled }, { [`custom-checkbox-${type}`]: type }, inlineClass]"
>
<input
:id="cbId"
class="custom-control-input"
:class="inputClasses"
type="checkbox"
:disabled="disabled"
v-model="model"
/>
<label :for="cbId" class="custom-control-label">
<slot>
<span v-if="inline">&nbsp;</span>
@ -17,62 +19,60 @@
</div>
</template>
<script>
export default {
name: "base-checkbox",
model: {
prop: "checked"
export default {
name: 'base-checkbox',
model: {
prop: 'checked',
},
props: {
checked: {
type: [Array, Boolean],
description: 'Whether checkbox is checked',
},
props: {
checked: {
type: [Array, Boolean],
description: "Whether checkbox is checked"
},
disabled: {
type: Boolean,
description: "Whether checkbox is disabled"
},
inline: {
type: Boolean,
description: "Whether checkbox is inline"
},
inputClasses: {
type: [String, Object, Array],
description: "Checkbox input classes"
},
type: {
type: String,
description: 'Checkbox type (e.g info, danger etc)'
}
disabled: {
type: Boolean,
description: 'Whether checkbox is disabled',
},
data() {
return {
cbId: "",
touched: false
};
inline: {
type: Boolean,
description: 'Whether checkbox is inline',
},
computed: {
model: {
get() {
return this.checked;
},
set(check) {
if (!this.touched) {
this.touched = true;
}
this.$emit("input", check);
}
},
inlineClass() {
if (this.inline) {
return `form-check-inline`;
}
return ''
}
inputClasses: {
type: [String, Object, Array],
description: 'Checkbox input classes',
},
created() {
this.cbId = Math.random()
.toString(16)
.slice(2);
type: {
type: String,
description: 'Checkbox type (e.g info, danger etc)',
},
},
data() {
return {
cbId: '',
touched: false,
}
};
},
computed: {
model: {
get() {
return this.checked
},
set(check) {
if (!this.touched) {
this.touched = true
}
this.$emit('input', check)
},
},
inlineClass() {
if (this.inline) {
return `form-check-inline`
}
return ''
},
},
created() {
this.cbId = Math.random().toString(16).slice(2)
},
}
</script>

View File

@ -1,24 +1,31 @@
<template>
<validation-provider :rules="rules" :name="name" v-bind="$attrs" v-slot="{errors, valid, invalid, validated}">
<validation-provider
:rules="rules"
:name="name"
v-bind="$attrs"
v-slot="{ errors, valid, invalid, validated }"
>
<b-form-group>
<slot name="label">
<label v-if="label" :class="labelClasses">
{{label}}
{{ label }}
</label>
</slot>
<div :class="[
{'input-group': hasIcon},
{'focused': focused},
{'input-group-alternative': alternative},
{'has-label': label || $slots.label},
inputGroupClasses
]">
<div
:class="[
{ 'input-group': hasIcon },
{ focused: focused },
{ 'input-group-alternative': alternative },
{ 'has-label': label || $slots.label },
inputGroupClasses,
]"
>
<div v-if="prependIcon || $slots.prepend" class="input-group-prepend">
<span class="input-group-text">
<slot name="prepend">
<i :class="prependIcon"></i>
</slot>
</span>
<span class="input-group-text">
<slot name="prepend">
<i :class="prependIcon"></i>
</slot>
</span>
</div>
<slot v-bind="slotData">
<input
@ -29,24 +36,29 @@
:valid="valid"
:required="required"
class="form-control"
:class="[{'is-valid': valid && validated && successMessage}, {'is-invalid': invalid && validated}, inputClasses]">
:class="[
{ 'is-valid': valid && validated && successMessage },
{ 'is-invalid': invalid && validated },
inputClasses,
]"
/>
</slot>
<div v-if="appendIcon || $slots.append" class="input-group-append">
<span class="input-group-text">
<slot name="append">
<i :class="appendIcon"></i>
</slot>
<slot name="append">
<i :class="appendIcon"></i>
</slot>
</span>
</div>
<slot name="infoBlock"></slot>
</div>
<slot name="success">
<div class="valid-feedback" v-if="valid && validated && successMessage">
{{successMessage}}
{{ successMessage }}
</div>
</slot>
<slot name="error">
<div v-if="errors[0]" class="invalid-feedback" style="display: block;">
<div v-if="errors[0]" class="invalid-feedback" style="display: block">
{{ errors[0] }}
</div>
</slot>
@ -54,123 +66,122 @@
</validation-provider>
</template>
<script>
export default {
inheritAttrs: false,
name: "base-input",
props: {
required: {
type: Boolean,
description: "Whether input is required (adds an asterix *)"
},
group: {
type: Boolean,
description: "Whether input is an input group (manual override in special cases)"
},
alternative: {
type: Boolean,
description: "Whether input is of alternative layout"
},
label: {
type: String,
description: "Input label (text before input)"
},
error: {
type: String,
description: "Input error (below input)"
},
successMessage: {
type: String,
description: "Input success message",
default: ''
},
labelClasses: {
type: String,
description: "Input label css classes",
default: "form-control-label"
},
inputClasses: {
type: String,
description: "Input css classes"
},
inputGroupClasses: {
type: String,
description: "Input group css classes"
},
value: {
type: [String, Number],
description: "Input value"
},
type: {
type: String,
description: "Input type",
default: "text"
},
appendIcon: {
type: String,
description: "Append icon (right)"
},
prependIcon: {
type: String,
description: "Prepend icon (left)"
},
rules: {
type: [String, Array, Object],
description: 'Vee validate validation rules',
default: ''
},
name: {
type: String,
description: 'Input name (used for validation)',
default: ''
}
export default {
inheritAttrs: false,
name: 'base-input',
props: {
required: {
type: Boolean,
description: 'Whether input is required (adds an asterix *)',
},
data() {
return {
focused: false
};
group: {
type: Boolean,
description: 'Whether input is an input group (manual override in special cases)',
},
computed: {
listeners() {
return {
...this.$listeners,
input: this.updateValue,
focus: this.onFocus,
blur: this.onBlur
};
},
slotData() {
return {
focused: this.focused,
error: this.error,
...this.listeners
};
},
hasIcon() {
const { append, prepend } = this.$slots;
return (
append !== undefined ||
prepend !== undefined ||
this.appendIcon !== undefined ||
this.prependIcon !== undefined ||
this.group
);
}
alternative: {
type: Boolean,
description: 'Whether input is of alternative layout',
},
methods: {
updateValue(evt) {
let value = evt.target.value;
this.$emit("input", value);
},
onFocus(evt) {
this.focused = true;
this.$emit("focus", evt);
},
onBlur(evt) {
this.focused = false;
this.$emit("blur", evt);
}
label: {
type: String,
description: 'Input label (text before input)',
},
error: {
type: String,
description: 'Input error (below input)',
},
successMessage: {
type: String,
description: 'Input success message',
default: '',
},
labelClasses: {
type: String,
description: 'Input label css classes',
default: 'form-control-label',
},
inputClasses: {
type: String,
description: 'Input css classes',
},
inputGroupClasses: {
type: String,
description: 'Input group css classes',
},
value: {
type: [String, Number],
description: 'Input value',
},
type: {
type: String,
description: 'Input type',
default: 'text',
},
appendIcon: {
type: String,
description: 'Append icon (right)',
},
prependIcon: {
type: String,
description: 'Prepend icon (left)',
},
rules: {
type: [String, Array, Object],
description: 'Vee validate validation rules',
default: '',
},
name: {
type: String,
description: 'Input name (used for validation)',
default: '',
},
},
data() {
return {
focused: false,
}
};
},
computed: {
listeners() {
return {
...this.$listeners,
input: this.updateValue,
focus: this.onFocus,
blur: this.onBlur,
}
},
slotData() {
return {
focused: this.focused,
error: this.error,
...this.listeners,
}
},
hasIcon() {
const { append, prepend } = this.$slots
return (
append !== undefined ||
prepend !== undefined ||
this.appendIcon !== undefined ||
this.prependIcon !== undefined ||
this.group
)
},
},
methods: {
updateValue(evt) {
let value = evt.target.value
this.$emit('input', value)
},
onFocus(evt) {
this.focused = true
this.$emit('focus', evt)
},
onBlur(evt) {
this.focused = false
this.$emit('blur', evt)
},
},
}
</script>
<style>
</style>
<style></style>

View File

@ -1,7 +1,5 @@
<template>
<div
class="custom-control custom-radio"
:class="[inlineClass, { disabled: disabled }]">
<div class="custom-control custom-radio" :class="[inlineClass, { disabled: disabled }]">
<input
:id="cbId"
class="custom-control-input"
@ -18,51 +16,49 @@
</div>
</template>
<script>
export default {
name: 'base-radio',
props: {
name: {
type: [String, Number],
description: 'Radio label'
},
disabled: {
type: Boolean,
description: 'Whether radio is disabled'
},
value: {
type: [String, Boolean],
description: 'Radio value'
},
inline: {
type: Boolean,
description: 'Whether radio is inline'
}
export default {
name: 'base-radio',
props: {
name: {
type: [String, Number],
description: 'Radio label',
},
data() {
return {
cbId: ''
};
disabled: {
type: Boolean,
description: 'Whether radio is disabled',
},
computed: {
model: {
get() {
return this.value;
},
set(value) {
this.$emit('input', value);
}
},
inlineClass() {
if (this.inline) {
return `form-check-inline`;
}
return '';
}
value: {
type: [String, Boolean],
description: 'Radio value',
},
created() {
this.cbId = Math.random()
.toString(16)
.slice(2);
inline: {
type: Boolean,
description: 'Whether radio is inline',
},
},
data() {
return {
cbId: '',
}
};
},
computed: {
model: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
},
},
inlineClass() {
if (this.inline) {
return `form-check-inline`
}
return ''
},
},
created() {
this.cbId = Math.random().toString(16).slice(2)
},
}
</script>

View File

@ -2,11 +2,11 @@
<b-row v-loading="true" id="loading"></b-row>
</template>
<script>
import Vue from 'vue';
import { Loading } from 'element-ui';
import Vue from 'vue'
import { Loading } from 'element-ui'
Vue.use(Loading.directive);
export default {};
Vue.use(Loading.directive)
export default {}
</script>
<style>
#loading {

View File

@ -1,125 +1,127 @@
<template>
<SlideYUpTransition :duration="animationDuration">
<b-modal class="modal fade"
ref="app-modal"
:size="size"
:hide-header="!$slots.header"
:modal-class="[{'modal-mini': type === 'mini'}, ...modalClasses]"
@mousedown.self="closeModal"
tabindex="-1"
role="dialog"
centered
@close="closeModal"
@hide="closeModal"
:header-class="headerClasses"
:footer-class="footerClasses"
:content-class="[gradient ? `bg-gradient-${gradient}` : '', ...modalContentClasses]"
:body-class="bodyClasses"
:aria-hidden="!show">
<slide-y-up-transition :duration="animationDuration">
<b-modal
class="modal fade"
ref="app-modal"
:size="size"
:hide-header="!$slots.header"
:modal-class="[{ 'modal-mini': type === 'mini' }, ...modalClasses]"
@mousedown.self="closeModal"
tabindex="-1"
role="dialog"
centered
@close="closeModal"
@hide="closeModal"
:header-class="headerClasses"
:footer-class="footerClasses"
:content-class="[gradient ? `bg-gradient-${gradient}` : '', ...modalContentClasses]"
:body-class="bodyClasses"
:aria-hidden="!show"
>
<template v-slot:modal-header>
<slot name="header"></slot>
<slot name="close-button">
<button type="button"
class="close"
v-if="showClose"
@click="closeModal"
data-dismiss="modal"
aria-label="Close">
<span :aria-hidden="!show">×</span>
</button>
</slot>
<slot name="close-button">
<button
type="button"
class="close"
v-if="showClose"
@click="closeModal"
data-dismiss="modal"
aria-label="Close"
>
<span :aria-hidden="!show">×</span>
</button>
</slot>
</template>
<slot />
<template v-slot:modal-footer>
<slot name="footer"></slot>
<slot name="footer"></slot>
</template>
</b-modal>
</SlideYUpTransition>
</slide-y-up-transition>
</template>
<script>
import { SlideYUpTransition } from "vue2-transitions";
import { SlideYUpTransition } from 'vue2-transitions'
export default {
name: "modal",
components: {
SlideYUpTransition
export default {
name: 'modal',
components: {
SlideYUpTransition,
},
props: {
show: Boolean,
showClose: {
type: Boolean,
default: true,
},
props: {
show: Boolean,
showClose: {
type: Boolean,
default: true
type: {
type: String,
default: '',
validator(value) {
let acceptedValues = ['', 'notice', 'mini']
return acceptedValues.indexOf(value) !== -1
},
type: {
type: String,
default: "",
validator(value) {
let acceptedValues = ["", "notice", "mini"];
return acceptedValues.indexOf(value) !== -1;
},
description: 'Modal type (notice|mini|"") '
description: 'Modal type (notice|mini|"") ',
},
modalClasses: {
type: [Object, String],
description: 'Modal dialog css classes',
},
size: {
type: String,
description: 'Modal size',
validator(value) {
let acceptedValues = ['', 'sm', 'lg']
return acceptedValues.indexOf(value) !== -1
},
modalClasses: {
type: [Object, String],
description: "Modal dialog css classes"
},
size: {
type: String,
description: 'Modal size',
validator(value) {
let acceptedValues = ["", "sm", "lg"];
return acceptedValues.indexOf(value) !== -1;
},
},
modalContentClasses: {
type: [Object, String],
description: "Modal dialog content css classes"
},
gradient: {
type: String,
description: "Modal gradient type (danger, primary etc)"
},
headerClasses: {
type: [Object, String],
description: "Modal Header css classes"
},
bodyClasses: {
type: [Object, String],
description: "Modal Body css classes"
},
footerClasses: {
type: [Object, String],
description: "Modal Footer css classes"
},
animationDuration: {
type: Number,
default: 500,
description: "Modal transition duration"
},
modalContentClasses: {
type: [Object, String],
description: 'Modal dialog content css classes',
},
gradient: {
type: String,
description: 'Modal gradient type (danger, primary etc)',
},
headerClasses: {
type: [Object, String],
description: 'Modal Header css classes',
},
bodyClasses: {
type: [Object, String],
description: 'Modal Body css classes',
},
footerClasses: {
type: [Object, String],
description: 'Modal Footer css classes',
},
animationDuration: {
type: Number,
default: 500,
description: 'Modal transition duration',
},
},
methods: {
closeModal() {
this.$emit('update:show', false)
this.$emit('close')
},
},
watch: {
show(val) {
if (val) {
this.$refs['app-modal'].show()
} else {
this.$refs['app-modal'].hide()
}
},
methods: {
closeModal() {
this.$emit("update:show", false);
this.$emit("close");
}
},
watch: {
show(val) {
if (val) {
this.$refs['app-modal'].show();
} else {
this.$refs['app-modal'].hide();
}
}
}
};
},
}
</script>
<style>
.modal-backdrop {
background-color: rgba(0, 0, 0, 0.6) !important;
}
.modal-backdrop {
background-color: rgba(0, 0, 0, 0.6) !important;
}
</style>

View File

@ -1,85 +1,80 @@
<template>
<!--Notice modal-->
<modal :show.sync="$store.state.modals"
modal-classes="modal-danger"
modal-content-classes="bg-gradient-danger">
<h6 slot="header" class="modal-title">Your attention is required</h6>
<!--Notice modal-->
<modal
:show.sync="$store.state.modals"
modal-classes="modal-danger"
modal-content-classes="bg-gradient-danger"
>
<h6 slot="header" class="modal-title">Your attention is required</h6>
<div class="py-3 text-center">
<form ref="form" @submit.stop.prevent="handleSubmit">
<b-form-group
label="Name"
label-for="name-input"
invalid-feedback="Name is required"
:state="nameState"
>
<b-form-input
id="name-input"
v-model="name"
:state="nameState"
required
></b-form-input>
</b-form-group>
</form>
</div>
<div class="py-3 text-center">
<form ref="form" @submit.stop.prevent="handleSubmit">
<b-form-group
label="Name"
label-for="name-input"
invalid-feedback="Name is required"
:state="nameState"
>
<b-form-input id="name-input" v-model="name" :state="nameState" required></b-form-input>
</b-form-group>
</form>
</div>
<template slot="footer">
<base-button type="white">Ok</base-button>
<base-button type="link" class="text-white ml-auto" @click="$store.state.modals = false">abbrechen</base-button>
</template>
</modal>
<template slot="footer">
<base-button type="white">Ok</base-button>
<base-button type="link" class="ml-auto" @click="$store.state.modals = false">
abbrechen
</base-button>
</template>
</modal>
</template>
<script>
export default {
name: 'modal',
data() {
return {
name: '',
nameState: null,
submittedNames: [],
}
},
/*Modal*/
checkFormValidity() {
const valid = this.$refs.form.checkValidity()
this.nameState = valid
return valid
},
resetModal() {
this.name = ''
this.nameState = null
},
handleOk(bvModalEvt) {
// Prevent modal from closing
bvModalEvt.preventDefault()
// Trigger submit handler
this.handleSubmit()
},
handleSubmit() {
// Exit when the form isn't valid
if (!this.checkFormValidity()) {
return
}
// Push the name to submitted names
this.submittedNames.push(this.name)
this.$store.state.modals = false
this.$store.commit('loginAsAdmin')
this.$router.push('/AdminOverview')
export default {
name: 'modal',
data() {
return {
name: '',
nameState: null,
submittedNames: []
}
},
/*Modal*/
checkFormValidity() {
const valid = this.$refs.form.checkValidity()
this.nameState = valid
return valid
},
resetModal() {
this.name = ''
this.nameState = null
},
handleOk(bvModalEvt) {
// Prevent modal from closing
bvModalEvt.preventDefault()
// Trigger submit handler
this.handleSubmit()
},
handleSubmit() {
// Exit when the form isn't valid
if (!this.checkFormValidity()) {
return
}
// Push the name to submitted names
this.submittedNames.push(this.name)
this.$store.state.modals = false
this.$store.commit('loginAsAdmin')
this.$router.push('/AdminOverview')
// Hide the modal manually
this.$nextTick(() => {
this.$bvModal.hide('modal-prevent-closing')
})
}
};
// Hide the modal manually
this.$nextTick(() => {
this.$bvModal.hide('modal-prevent-closing')
})
},
}
</script>
<style>
.modal-backdrop {
background-color: rgba(0, 0, 0, 0.6) !important;
}
.modal-backdrop {
background-color: rgba(0, 0, 0, 0.6) !important;
}
</style>

View File

@ -18,10 +18,7 @@
</button>
</slot>
<b-navbar-toggle
target="nav-text-collapse"
@click.stop="toggleMenu">
</b-navbar-toggle>
<b-navbar-toggle target="nav-text-collapse" @click.stop="toggleMenu"></b-navbar-toggle>
<b-collapse
is-nav
@ -29,7 +26,8 @@
class="navbar-custom-collapse collapse"
:class="menuClasses"
:visible="show"
v-click-outside="closeMenu">
v-click-outside="closeMenu"
>
<slot :close-menu="closeMenu"></slot>
</b-collapse>
</div>
@ -43,29 +41,28 @@ export default {
type: Boolean,
default: false,
description:
'Whether navbar menu is shown (valid for viewports < specified by `expand` prop)'
'Whether navbar menu is shown (valid for viewports < specified by `expand` prop)',
},
transparent: {
type: Boolean,
default: false,
description: 'Whether navbar is transparent'
description: 'Whether navbar is transparent',
},
expand: {
type: String,
default: 'lg',
description: 'Breakpoint where nav should expand'
description: 'Breakpoint where nav should expand',
},
menuClasses: {
type: [String, Object, Array],
default: '',
description:
'Navbar menu (items) classes. Can be used to align menu items to the right/left'
description: 'Navbar menu (items) classes. Can be used to align menu items to the right/left',
},
containerClasses: {
type: [String, Object, Array],
default: 'container',
description:
'Container classes. Can be used to control container classes (contains both navbar brand and menu items)'
'Container classes. Can be used to control container classes (contains both navbar brand and menu items)',
},
type: {
type: String,
@ -81,43 +78,43 @@ export default {
'primary',
'light',
'info',
'vue'
].includes(value);
'vue',
].includes(value)
},
description: 'Navbar color type'
}
description: 'Navbar color type',
},
},
model: {
prop: 'show',
event: 'change'
event: 'change',
},
computed: {
classes() {
let color = `bg-${this.type}`;
let color = `bg-${this.type}`
let classes = [
{ 'navbar-transparent': this.transparent },
{ [`navbar-expand-${this.expand}`]: this.expand }
];
{ [`navbar-expand-${this.expand}`]: this.expand },
]
if (this.position) {
classes.push(`navbar-${this.position}`);
classes.push(`navbar-${this.position}`)
}
if (!this.transparent) {
classes.push(color);
classes.push(color)
}
return classes;
return classes
},
hasMenu() {
return this.$slots.default;
}
return this.$slots.default
},
},
methods: {
toggleMenu() {
this.$emit('change', !this.show);
this.$emit('change', !this.show)
},
closeMenu() {
this.$emit('change', false);
}
}
};
this.$emit('change', false)
},
},
}
</script>
<style></style>

View File

@ -15,7 +15,7 @@
</template>
<script>
export default {
name: 'navbar-toggle-button'
};
name: 'navbar-toggle-button',
}
</script>
<style></style>

View File

@ -10,7 +10,7 @@
>
<span class="navbar-toggler-icon"></span>
<slot>
<span></span>
<span></span>
</slot>
</button>
</template>
@ -19,14 +19,14 @@ export default {
props: {
target: {
type: [String, Number],
description: 'Button target element'
description: 'Button target element',
},
toggled: {
type: Boolean,
default: false,
description: 'Whether button is toggled'
}
}
};
description: 'Whether button is toggled',
},
},
}
</script>
<style></style>

View File

@ -3,207 +3,192 @@
@click="tryClose"
data-notify="container"
class="alert alert-notify alert-dismissible"
:class="[
{ 'alert-with-icon': icon },
verticalAlign,
horizontalAlign,
alertType
]"
:class="[{ 'alert-with-icon': icon }, verticalAlign, horizontalAlign, alertType]"
role="alert"
:style="customPosition"
data-notify-position="top-center"
>
<template v-if="icon || $slots.icon">
<slot name="icon">
<span class="alert-icon" data-notify="icon">
<i :class="icon"></i>
</span>
<span class="alert-icon" data-notify="icon">
<i :class="icon"></i>
</span>
</slot>
</template>
<span class="alert-text">
<span v-if="title" class="title">
<b>{{ title }}<br/></b>
<b>
{{ title }}
<br />
</b>
</span>
<span v-if="message" v-html="message"></span>
<content-render
v-if="!message && component"
:component="component"
></content-render>
<content-render v-if="!message && component" :component="component"></content-render>
</span>
<slot name="dismiss-icon">
<button type="button"
class="close"
data-dismiss="alert"
aria-label="Close"
@click="close">
<button type="button" class="close" data-dismiss="alert" aria-label="Close" @click="close">
<span aria-hidden="true">×</span>
</button>
</slot>
</div>
</template>
<script>
export default {
name: 'notification',
components: {
contentRender: {
props: ['component'],
render: function (createElement) {
return createElement(this.component)
}
}
export default {
name: 'notification',
components: {
contentRender: {
props: ['component'],
render: function (createElement) {
return createElement(this.component)
},
},
props: {
message: String,
title: {
type: String,
description: 'Notification title'
},
icon: {
type: String,
description: 'Notification icon'
},
verticalAlign: {
type: String,
default: 'top',
validator: value => {
let acceptedValues = ['top', 'bottom'];
return acceptedValues.indexOf(value) !== -1;
},
description: 'Vertical alignment of notification (top|bottom)'
},
horizontalAlign: {
type: String,
default: 'right',
validator: value => {
let acceptedValues = ['left', 'center', 'right'];
return acceptedValues.indexOf(value) !== -1;
},
description: 'Horizontal alignment of notification (left|center|right)'
},
type: {
type: String,
default: 'info',
validator: value => {
let acceptedValues = [
'default',
'info',
'primary',
'danger',
'warning',
'success'
];
return acceptedValues.indexOf(value) !== -1;
},
description: 'Notification type of notification (default|info|primary|danger|warning|success)'
},
timeout: {
type: Number,
default: 5000,
validator: value => {
return value >= 0;
},
description: 'Notification timeout (closes after X milliseconds). Default is 5000 (5s)'
},
timestamp: {
type: Date,
default: () => new Date(),
description: 'Notification timestamp (used internally to handle notification removal correctly)'
},
component: {
type: [Object, Function],
description: 'Custom content component. Cane be a `.vue` component or render function'
},
showClose: {
type: Boolean,
default: true,
description: 'Whether to show close button'
},
closeOnClick: {
type: Boolean,
default: true,
description: 'Whether to close notification when clicking it\' body'
},
clickHandler: {
type: Function,
description: 'Custom notification click handler'
}
},
props: {
message: String,
title: {
type: String,
description: 'Notification title',
},
data() {
return {
elmHeight: 0
};
icon: {
type: String,
description: 'Notification icon',
},
computed: {
hasIcon() {
return this.icon && this.icon.length > 0;
verticalAlign: {
type: String,
default: 'top',
validator: (value) => {
let acceptedValues = ['top', 'bottom']
return acceptedValues.indexOf(value) !== -1
},
alertType() {
return `alert-${this.type}`;
},
customPosition() {
let initialMargin = 20;
let alertHeight = this.elmHeight + 10;
let sameAlertsCount = this.$notifications.state.filter(alert => {
return (
alert.horizontalAlign === this.horizontalAlign &&
alert.verticalAlign === this.verticalAlign &&
alert.timestamp <= this.timestamp
);
}).length;
if (this.$notifications.settings.overlap) {
sameAlertsCount = 1;
}
let pixels = (sameAlertsCount - 1) * alertHeight + initialMargin;
let styles = {};
if (this.verticalAlign === 'top') {
styles.top = `${pixels}px`;
} else {
styles.bottom = `${pixels}px`;
}
return styles;
}
description: 'Vertical alignment of notification (top|bottom)',
},
methods: {
close() {
this.$emit('close', this.timestamp);
horizontalAlign: {
type: String,
default: 'right',
validator: (value) => {
let acceptedValues = ['left', 'center', 'right']
return acceptedValues.indexOf(value) !== -1
},
tryClose(evt) {
if (this.clickHandler) {
this.clickHandler(evt, this);
}
if (this.closeOnClick) {
this.close();
}
}
description: 'Horizontal alignment of notification (left|center|right)',
},
mounted() {
this.elmHeight = this.$el.clientHeight;
if (this.timeout) {
setTimeout(this.close, this.timeout);
}
type: {
type: String,
default: 'info',
validator: (value) => {
let acceptedValues = ['default', 'info', 'primary', 'danger', 'warning', 'success']
return acceptedValues.indexOf(value) !== -1
},
description:
'Notification type of notification (default|info|primary|danger|warning|success)',
},
timeout: {
type: Number,
default: 5000,
validator: (value) => {
return value >= 0
},
description: 'Notification timeout (closes after X milliseconds). Default is 5000 (5s)',
},
timestamp: {
type: Date,
default: () => new Date(),
description:
'Notification timestamp (used internally to handle notification removal correctly)',
},
component: {
type: [Object, Function],
description: 'Custom content component. Cane be a `.vue` component or render function',
},
showClose: {
type: Boolean,
default: true,
description: 'Whether to show close button',
},
closeOnClick: {
type: Boolean,
default: true,
description: "Whether to close notification when clicking it' body",
},
clickHandler: {
type: Function,
description: 'Custom notification click handler',
},
},
data() {
return {
elmHeight: 0,
}
};
},
computed: {
hasIcon() {
return this.icon && this.icon.length > 0
},
alertType() {
return `alert-${this.type}`
},
customPosition() {
let initialMargin = 20
let alertHeight = this.elmHeight + 10
let sameAlertsCount = this.$notifications.state.filter((alert) => {
return (
alert.horizontalAlign === this.horizontalAlign &&
alert.verticalAlign === this.verticalAlign &&
alert.timestamp <= this.timestamp
)
}).length
if (this.$notifications.settings.overlap) {
sameAlertsCount = 1
}
let pixels = (sameAlertsCount - 1) * alertHeight + initialMargin
let styles = {}
if (this.verticalAlign === 'top') {
styles.top = `${pixels}px`
} else {
styles.bottom = `${pixels}px`
}
return styles
},
},
methods: {
close() {
this.$emit('close', this.timestamp)
},
tryClose(evt) {
if (this.clickHandler) {
this.clickHandler(evt, this)
}
if (this.closeOnClick) {
this.close()
}
},
},
mounted() {
this.elmHeight = this.$el.clientHeight
if (this.timeout) {
setTimeout(this.close, this.timeout)
}
},
}
</script>
<style lang="scss">
.notifications .alert {
position: fixed;
z-index: 10000;
.notifications .alert {
position: fixed;
z-index: 10000;
&[data-notify='container'] {
max-width: 500px;
}
&.center {
margin: 0 auto;
}
&.left {
left: 20px;
}
&.right {
right: 20px;
}
&[data-notify='container'] {
max-width: 500px;
}
&.center {
margin: 0 auto;
}
&.left {
left: 20px;
}
&.right {
right: 20px;
}
}
</style>

View File

@ -1,55 +1,52 @@
<template>
<div class="notifications">
<slide-y-up-transition :duration="transitionDuration"
group
mode="out-in">
<slide-y-up-transition :duration="transitionDuration" group mode="out-in">
<notification
v-for="notification in notifications"
v-bind="notification"
:clickHandler="notification.onClick"
:key="notification.timestamp.getTime()"
@close="removeNotification"
>
</notification>
></notification>
</slide-y-up-transition>
</div>
</template>
<script>
import Notification from './Notification.vue';
import { SlideYUpTransition } from 'vue2-transitions';
import Notification from './Notification.vue'
import { SlideYUpTransition } from 'vue2-transitions'
export default {
components: {
SlideYUpTransition,
Notification
export default {
components: {
SlideYUpTransition,
Notification,
},
props: {
transitionDuration: {
type: Number,
default: 200,
},
props: {
transitionDuration: {
type: Number,
default: 200
},
overlap: {
type: Boolean,
default: false
}
overlap: {
type: Boolean,
default: false,
},
data() {
return {
notifications: this.$notifications.state
};
},
methods: {
removeNotification(timestamp) {
this.$notifications.removeNotification(timestamp);
}
},
created() {
this.$notifications.settings.overlap = this.overlap;
},
watch: {
overlap: function (newVal) {
this.$notifications.settings.overlap = newVal;
}
},
data() {
return {
notifications: this.$notifications.state,
}
};
},
methods: {
removeNotification(timestamp) {
this.$notifications.removeNotification(timestamp)
},
},
created() {
this.$notifications.settings.overlap = this.overlap
},
watch: {
overlap: function (newVal) {
this.$notifications.settings.overlap = newVal
},
},
}
</script>

View File

@ -1,4 +1,4 @@
import Notifications from './Notifications.vue';
import Notifications from './Notifications.vue'
const NotificationStore = {
state: [], // here the notifications will be added
@ -9,58 +9,58 @@ const NotificationStore = {
type: 'info',
timeout: 5000,
closeOnClick: true,
showClose: true
showClose: true,
},
setOptions(options) {
this.settings = Object.assign(this.settings, options);
this.settings = Object.assign(this.settings, options)
},
removeNotification(timestamp) {
const indexToDelete = this.state.findIndex(n => n.timestamp === timestamp);
const indexToDelete = this.state.findIndex((n) => n.timestamp === timestamp)
if (indexToDelete !== -1) {
this.state.splice(indexToDelete, 1);
this.state.splice(indexToDelete, 1)
}
},
addNotification(notification) {
if (typeof notification === 'string' || notification instanceof String) {
notification = { message: notification };
notification = { message: notification }
}
notification.timestamp = new Date();
notification.timestamp = new Date()
notification.timestamp.setMilliseconds(
notification.timestamp.getMilliseconds() + this.state.length
);
notification = Object.assign({}, this.settings, notification);
this.state.push(notification);
notification.timestamp.getMilliseconds() + this.state.length,
)
notification = Object.assign({}, this.settings, notification)
this.state.push(notification)
},
notify(notification) {
if (Array.isArray(notification)) {
notification.forEach(notificationInstance => {
this.addNotification(notificationInstance);
});
notification.forEach((notificationInstance) => {
this.addNotification(notificationInstance)
})
} else {
this.addNotification(notification);
this.addNotification(notification)
}
}
};
},
}
const NotificationsPlugin = {
install(Vue, options) {
let app = new Vue({
data: {
notificationStore: NotificationStore
notificationStore: NotificationStore,
},
methods: {
notify(notification) {
this.notificationStore.notify(notification);
}
}
});
Vue.prototype.$notify = app.notify;
Vue.prototype.$notifications = app.notificationStore;
Vue.component('Notifications', Notifications);
this.notificationStore.notify(notification)
},
},
})
Vue.prototype.$notify = app.notify
Vue.prototype.$notifications = app.notificationStore
Vue.component('Notifications', Notifications)
if (options) {
NotificationStore.setOptions(options);
NotificationStore.setOptions(options)
}
}
};
},
}
export default NotificationsPlugin;
export default NotificationsPlugin

View File

@ -1,12 +1,8 @@
<template>
<vue-bootstrap-typeahead
v-model="query"
:data="users"
@change="getUser"
/>
<vue-bootstrap-typeahead v-model="query" :data="users" @change="getUser" />
</template>
<script>
import VueBootstrapTypeahead from 'vue-bootstrap-typeahead'
import VueBootstrapTypeahead from 'vue-bootstrap-typeahead'
// Global registration
//Vue.component('vue-bootstrap-typeahead', VueBootstrapTypeahead)
@ -15,71 +11,71 @@
// Local registration
export default {
name: "SearchUser",
components: {
VueBootstrapTypeahead
},
data() {
return {
user: "",
users: [
"Bob",
"Alice",
"Bernd",
"Dario",
"Alex",
"Pauls",
"Ulf",
"Delaware",
"Florida",
"Georgia",
"Hawaii",
"Idaho",
"Illnois",
"Indiana",
"Iowa",
"Kansas",
"Kentucky",
"Louisiana",
"Maine",
"Maryland",
"Massachusetts",
"Michigan",
"Minnesota",
"Mississippi",
"Missouri",
"Montana",
"Nebraska",
"Nevada",
"New Hampshire",
"New Jersey",
"New Mexico",
"New York",
"North Carolina",
"North Dakota",
"Ohio",
"Oklahoma",
"Oregon",
"Pennsylvania",
"Rhode Island",
"South Carolina",
"South Dakota",
"Tennessee",
"Texas",
"Utah",
"Vermont",
"Virginia",
"Washington",
"West Virginia",
"Wisconsin",
"Wyoming"
]
};
},
methods: {
getUser() {
alert(this.data.user)
}
name: 'SearchUser',
components: {
VueBootstrapTypeahead,
},
data() {
return {
user: '',
users: [
'Bob',
'Alice',
'Bernd',
'Dario',
'Alex',
'Pauls',
'Ulf',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illnois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Carolina',
'North Dakota',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming',
],
}
};
</script>
},
methods: {
getUser() {
alert(this.data.user)
},
},
}
</script>

View File

@ -1,137 +1,133 @@
<template>
<nav class="navbar navbar-vertical fixed-left navbar-expand-md navbar-light bg-transparent" id="sidenav-main">
<div class="container-fluid">
<!--Toggler-->
<navbar-toggle-button @click.native="showSidebar">
</navbar-toggle-button>
<router-link class="navbar-brand" to="/">
<img :src="logo" class="navbar-brand-img" alt="...">
<nav
class="navbar navbar-vertical fixed-left navbar-expand-md navbar-light bg-transparent"
id="sidenav-main"
>
<div class="container-fluid">
<!--Toggler-->
<navbar-toggle-button @click.native="showSidebar"></navbar-toggle-button>
<div class="navbar-brand">
<img :src="logo" class="navbar-brand-img" alt="..." />
</div>
<b-row class="text-center">
<b-col>{{ $n($store.state.user.balance) }} GDD</b-col>
</b-row>
<slot name="mobile-right">
<ul class="nav align-items-center d-md-none">
<base-dropdown class="nav-item" menu-on-right tag="li" title-tag="a">
<a slot="title-container" class="nav-link" href="#" role="button">
<div class="media align-items-center">
<span class="avatar avatar-sm">
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
</span>
</div>
</a>
xx
<router-link to="/overview" class="dropdown-item text-lg text-muted">
<i class="ni ni-single-02"></i>
<span>{{ $t('site.overview.account_overview') }}</span>
</router-link>
<slot name="mobile-right">
<ul class="nav align-items-center d-md-none">
<base-dropdown class="nav-item" menu-on-right tag="li" title-tag="a">
<a slot="title-container" class="nav-link" href="#" role="button">
<div class="media align-items-center">
<span class="avatar avatar-sm rounded-circle">
<img alt="Image placeholder" src="img/theme/team-1.jpg">
</span>
</div>
</a>
<router-link to="/KontoOverview" class="dropdown-item text-lg text-muted">
<i class="ni ni-single-02"></i>
<span>{{ $t('site.overview.account_overview')}}</span>
</router-link>
<router-link to="/profile" class="dropdown-item text-lg text-muted">
<i class="ni ni-single-02"></i>
<span>{{ $t('site.navbar.my-profil')}}</span>
</router-link>
<router-link to="/profileedit" class="dropdown-item text-lg text-muted">
<i class="ni ni-settings-gear-65"></i>
<span>{{ $t('site.navbar.settings') }}</span>
</router-link>
<router-link to="/profile" class="dropdown-item text-lg text-muted">
<i class="ni ni-single-02"></i>
<span>{{ $t('site.navbar.my-profil') }}</span>
</router-link>
<router-link to="/profileedit" class="dropdown-item text-lg text-muted">
<i class="ni ni-settings-gear-65"></i>
<span>{{ $t('site.navbar.settings') }}</span>
</router-link>
<router-link to="/transactions" class="dropdown-item text-lg text-muted">
<i class="ni ni-calendar-grid-58"></i>
<span>Transactions</span>
</router-link>
<!--
<router-link to="/activity" class="dropdown-item text-lg text-muted">
<i class="ni ni-calendar-grid-58"></i>
<span>{{ $t('site.navbar.activity') }}</span>
</router-link>
<div class="dropdown-divider"></div>
<router-link @click="logout" class="dropdown-item text-lg text-muted">
<i class="ni ni-support-16"></i>
<span>{{ $t('logout') }}</span>
</router-link>
</base-dropdown>
</ul>
</slot>
<slot></slot>
<div v-show="$sidebar.showSidebar" class="navbar-collapse collapse show" id="sidenav-collapse-main">
<div class="navbar-collapse-header d-md-none">
<div class="row">
<div class="col-6 collapse-brand">
<router-link to="/">
<img :src="logo">
</router-link>
</div>
<div class="col-6 collapse-close">
<navbar-toggle-button @click.native="closeSidebar"></navbar-toggle-button>
</div>
</div>
</div>
<ul class="navbar-nav">
<slot name="links">
</slot>
</ul>
<hr class="my-3">
<ul class="navbar-nav mb-md-3">
<li class="nav-item">
<a class="nav-link text-lg bg-light" href="#!" @click="logout">
{{ $t('logout') }}
</a>
</li>
</ul>
-->
<div class="dropdown-divider"></div>
<div @click="logout" class="dropdown-item text-lg text-muted">
<i class="ni ni-support-16"></i>
{{ $t('logout') }}
</div>
</base-dropdown>
</ul>
</slot>
<slot></slot>
<div
v-show="$sidebar.showSidebar"
class="navbar-collapse collapse show"
id="sidenav-collapse-main"
>
<div class="navbar-collapse-header d-md-none">
<div class="row">
<div class="col-6 collapse-brand">
<router-link to="/overview">
<img :src="logo" />
</router-link>
</div>
</nav>
<div class="col-6 collapse-close">
<navbar-toggle-button @click.native="closeSidebar"></navbar-toggle-button>
</div>
</div>
</div>
<ul class="navbar-nav">
<slot name="links"></slot>
</ul>
<hr class="my-3" />
<ul class="navbar-nav mb-md-3">
<li class="nav-item">
<a class="nav-link text-lg" href="#!" @click="logout">
{{ $t('logout') }}
</a>
</li>
</ul>
</div>
</div>
</nav>
</template>
<script>
import NavbarToggleButton from '@/components/NavbarToggleButton'
import NavbarToggleButton from '@/components/NavbarToggleButton'
import VueQrcode from 'vue-qrcode'
export default {
name: 'sidebar',
components: {
NavbarToggleButton
export default {
name: 'sidebar',
components: {
NavbarToggleButton,
VueQrcode,
},
props: {
logo: {
type: String,
default: 'img/brand/green.png',
description: 'Gradido Sidebar app logo',
},
props: {
logo: {
type: String,
default: 'img/brand/green.png',
description: 'Sidebar app logo'
},
autoClose: {
type: Boolean,
default: true,
description: 'Whether sidebar should autoclose on mobile when clicking an item'
}
autoClose: {
type: Boolean,
default: true,
description: 'Whether sidebar should autoclose on mobile when clicking an item',
},
provide() {
return {
autoClose: this.autoClose
};
},
methods: {
closeSidebar() {
this.$sidebar.displaySidebar(false)
},
showSidebar() {
this.$sidebar.displaySidebar(true)
},
setLocale(locale) {
this.$i18n.locale = locale
//this.$router.push({
// params: { lang: locale }
//})
//this.hideDropdown()
},
logout(){
//console.log("DashboardNavbar.vue user logout() : ")
this.$store.dispatch('logout')
}
},
beforeDestroy() {
if (this.$sidebar.showSidebar) {
this.$sidebar.showSidebar = false;
}
},
provide() {
return {
autoClose: this.autoClose,
}
};
},
methods: {
closeSidebar() {
this.$sidebar.displaySidebar(false)
},
showSidebar() {
this.$sidebar.displaySidebar(true)
},
logout() {
this.$store.dispatch('logout')
},
},
beforeDestroy() {
if (this.$sidebar.showSidebar) {
this.$sidebar.showSidebar = false
}
},
}
</script>

View File

@ -15,31 +15,28 @@
>
<template v-if="addLink">
<span class="nav-link-text">
{{ link.name }} <b class="caret"></b>
{{ link.name }}
<b class="caret"></b>
</span>
</template>
<template v-else>
<i :class="link.icon"></i>
<span class="nav-link-text">{{ link.name }} <b class="caret"></b></span>
<span class="nav-link-text">
{{ link.name }}
<b class="caret"></b>
</span>
</template>
</a>
<collapse-transition>
<div
v-if="$slots.default || this.isMenu"
v-show="!collapsed"
class="collapse show"
>
<div v-if="$slots.default || this.isMenu" v-show="!collapsed" class="collapse show">
<ul class="nav nav-sm flex-column">
<slot></slot>
</ul>
</div>
</collapse-transition>
<slot
name="title"
v-if="children.length === 0 && !$slots.default && link.path"
>
<slot name="title" v-if="children.length === 0 && !$slots.default && link.path">
<component
:to="link.path"
@click.native="linkClick"
@ -61,19 +58,19 @@
</b-nav-item>
</template>
<script>
import { CollapseTransition } from 'vue2-transitions';
import { CollapseTransition } from 'vue2-transitions'
export default {
name: 'sidebar-item',
components: {
CollapseTransition
CollapseTransition,
},
props: {
menu: {
type: Boolean,
default: false,
description:
"Whether the item is a menu. Most of the item it's not used and should be used only if you want to override the default behavior."
"Whether the item is a menu. Most of the item it's not used and should be used only if you want to override the default behavior.",
},
link: {
type: Object,
@ -81,115 +78,109 @@ export default {
return {
name: '',
path: '',
children: []
};
children: [],
}
},
description:
'Sidebar link. Can contain name, path, icon and other attributes. See examples for more info'
}
'Sidebar link. Can contain name, path, icon and other attributes. See examples for more info',
},
},
provide() {
return {
addLink: this.addChild,
removeLink: this.removeChild
};
removeLink: this.removeChild,
}
},
inject: {
addLink: { default: null },
removeLink: { default: null },
autoClose: {
default: true
}
default: true,
},
},
data() {
return {
children: [],
collapsed: true
};
collapsed: true,
}
},
computed: {
baseComponent() {
return this.isMenu || this.link.isRoute ? 'li' : 'router-link';
return this.isMenu || this.link.isRoute ? 'li' : 'router-link'
},
linkPrefix() {
if (this.link.name) {
let words = this.link.name.split(' ');
return words.map(word => word.substring(0, 1)).join('');
let words = this.link.name.split(' ')
return words.map((word) => word.substring(0, 1)).join('')
}
return ''
},
isMenu() {
return this.children.length > 0 || this.menu === true;
return this.children.length > 0 || this.menu === true
},
isActive() {
if (this.$route && this.$route.path) {
let matchingRoute = this.children.find(c =>
this.$route.path.startsWith(c.link.path)
);
let matchingRoute = this.children.find((c) => this.$route.path.startsWith(c.link.path))
if (matchingRoute !== undefined) {
return true;
return true
}
}
return false;
}
return false
},
},
methods: {
addChild(item) {
const index = this.$slots.default.indexOf(item.$vnode);
this.children.splice(index, 0, item);
const index = this.$slots.default.indexOf(item.$vnode)
this.children.splice(index, 0, item)
},
removeChild(item) {
const tabs = this.children;
const index = tabs.indexOf(item);
tabs.splice(index, 1);
const tabs = this.children
const index = tabs.indexOf(item)
tabs.splice(index, 1)
},
elementType(link, isParent = true) {
if (link.isRoute === false) {
return isParent ? 'li' : 'a';
return isParent ? 'li' : 'a'
} else {
return 'router-link';
return 'router-link'
}
},
linkAbbreviation(name) {
const matches = name.match(/\b(\w)/g);
return matches.join('');
const matches = name.match(/\b(\w)/g)
return matches.join('')
},
linkClick() {
if (
this.autoClose &&
this.$sidebar &&
this.$sidebar.showSidebar === true
) {
this.$sidebar.displaySidebar(false);
if (this.autoClose && this.$sidebar && this.$sidebar.showSidebar === true) {
this.$sidebar.displaySidebar(false)
}
},
collapseMenu() {
this.collapsed = !this.collapsed;
this.collapsed = !this.collapsed
},
collapseSubMenu(link) {
link.collapsed = !link.collapsed;
}
link.collapsed = !link.collapsed
},
},
mounted() {
if (this.addLink) {
this.addLink(this);
this.addLink(this)
}
if (this.link.collapsed !== undefined) {
this.collapsed = this.link.collapsed;
this.collapsed = this.link.collapsed
}
if (this.isActive && this.isMenu) {
this.collapsed = false;
this.collapsed = false
}
},
destroyed() {
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el);
this.$el.parentNode.removeChild(this.$el)
}
if (this.removeLink) {
this.removeLink(this);
this.removeLink(this)
}
}
};
},
}
</script>
<style>
.sidebar-menu-item {

View File

@ -1,43 +1,43 @@
import Sidebar from './SideBar.vue';
import SidebarItem from './SidebarItem.vue';
import Sidebar from './SideBar.vue'
import SidebarItem from './SidebarItem.vue'
const SidebarStore = {
showSidebar: false,
sidebarLinks: [],
isMinimized: false,
displaySidebar(value) {
this.showSidebar = value;
this.showSidebar = value
},
toggleMinimize() {
document.body.classList.toggle('sidebar-mini');
document.body.classList.toggle('sidebar-mini')
// we simulate the window Resize so the charts will get updated in realtime.
const simulateWindowResize = setInterval(() => {
window.dispatchEvent(new Event('resize'));
}, 180);
window.dispatchEvent(new Event('resize'))
}, 180)
// we stop the simulation of Window Resize after the animations are completed
setTimeout(() => {
clearInterval(simulateWindowResize);
}, 1000);
clearInterval(simulateWindowResize)
}, 1000)
this.isMinimized = !this.isMinimized;
}
};
this.isMinimized = !this.isMinimized
},
}
const SidebarPlugin = {
install(Vue, options) {
if (options && options.sidebarLinks) {
SidebarStore.sidebarLinks = options.sidebarLinks;
SidebarStore.sidebarLinks = options.sidebarLinks
}
let app = new Vue({
data: {
sidebarStore: SidebarStore
}
});
Vue.prototype.$sidebar = app.sidebarStore;
Vue.component('side-bar', Sidebar);
Vue.component('sidebar-item', SidebarItem);
}
};
sidebarStore: SidebarStore,
},
})
Vue.prototype.$sidebar = app.sidebarStore
Vue.component('side-bar', Sidebar)
Vue.component('sidebar-item', SidebarItem)
},
}
export default SidebarPlugin;
export default SidebarPlugin

View File

@ -16,18 +16,18 @@ export default {
inject: ['addTab', 'removeTab'],
data() {
return {
active: false
};
active: false,
}
},
mounted() {
this.addTab(this);
this.addTab(this)
},
destroyed() {
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el);
this.$el.parentNode.removeChild(this.$el)
}
this.removeTab(this);
}
};
this.removeTab(this)
},
}
</script>
<style></style>

View File

@ -4,7 +4,7 @@
:class="[
{ 'col-md-4': vertical && !tabNavWrapperClasses },
{ 'col-12': centered && !tabNavWrapperClasses },
tabNavWrapperClasses
tabNavWrapperClasses,
]"
>
<b-nav
@ -14,7 +14,7 @@
`nav-pills-${type}`,
{ 'flex-column': vertical },
{ 'justify-content-center': centered },
tabNavClasses
tabNavClasses,
]"
>
<b-nav-item
@ -28,7 +28,7 @@
@click.prevent="activateTab(tab)"
:aria-expanded="tab.active"
>
<tab-item-content :tab="tab"> </tab-item-content>
<tab-item-content :tab="tab"></tab-item-content>
</b-nav-item>
</b-nav>
</div>
@ -37,7 +37,7 @@
:class="[
{ 'tab-space': !vertical },
{ 'col-md-8': vertical && !tabContentClasses },
tabContentClasses
tabContentClasses,
]"
>
<slot></slot>
@ -52,110 +52,104 @@ export default {
TabItemContent: {
props: ['tab'],
render(h) {
return h('div', [this.tab.$slots.title || this.tab.title]);
}
}
return h('div', [this.tab.$slots.title || this.tab.title])
},
},
},
provide() {
return {
addTab: this.addTab,
removeTab: this.removeTab
};
removeTab: this.removeTab,
}
},
props: {
type: {
type: String,
default: 'primary',
validator: value => {
let acceptedValues = [
'primary',
'info',
'success',
'warning',
'danger'
];
return acceptedValues.indexOf(value) !== -1;
}
validator: (value) => {
let acceptedValues = ['primary', 'info', 'success', 'warning', 'danger']
return acceptedValues.indexOf(value) !== -1
},
},
activeTab: {
type: String,
default: '',
description: 'Active tab name'
description: 'Active tab name',
},
tabNavWrapperClasses: {
type: [String, Object],
default: '',
description: 'ul wrapper css classes'
description: 'ul wrapper css classes',
},
tabNavClasses: {
type: [String, Object],
default: '',
description: 'ul css classes'
description: 'ul css classes',
},
tabContentClasses: {
type: [String, Object],
default: '',
description: 'tab content css classes'
description: 'tab content css classes',
},
vertical: Boolean,
centered: Boolean,
value: String
value: String,
},
data() {
return {
tabs: []
};
tabs: [],
}
},
methods: {
findAndActivateTab(title) {
let tabToActivate = this.tabs.find(t => t.title === title);
let tabToActivate = this.tabs.find((t) => t.title === title)
if (tabToActivate) {
this.activateTab(tabToActivate);
this.activateTab(tabToActivate)
}
},
activateTab(tab) {
if (this.handleClick) {
this.handleClick(tab);
this.handleClick(tab)
}
this.deactivateTabs();
tab.active = true;
this.deactivateTabs()
tab.active = true
},
deactivateTabs() {
this.tabs.forEach(tab => {
tab.active = false;
});
this.tabs.forEach((tab) => {
tab.active = false
})
},
addTab(tab) {
const index = this.$slots.default.indexOf(tab.$vnode);
const index = this.$slots.default.indexOf(tab.$vnode)
if (!this.activeTab && index === 0) {
tab.active = true;
tab.active = true
}
if (this.activeTab === tab.name) {
tab.active = true;
tab.active = true
}
this.tabs.splice(index, 0, tab);
this.tabs.splice(index, 0, tab)
},
removeTab(tab) {
const tabs = this.tabs;
const index = tabs.indexOf(tab);
const tabs = this.tabs
const index = tabs.indexOf(tab)
if (index > -1) {
tabs.splice(index, 1);
tabs.splice(index, 1)
}
}
},
},
mounted() {
this.$nextTick(() => {
if (this.value) {
this.findAndActivateTab(this.value);
this.findAndActivateTab(this.value)
}
});
})
},
watch: {
value(newVal) {
this.findAndActivateTab(newVal);
}
}
};
this.findAndActivateTab(newVal)
},
},
}
</script>
<style scoped></style>

View File

@ -1,30 +1,30 @@
import BaseCheckbox from './Inputs/BaseCheckbox.vue';
import BaseAlert from './BaseAlert.vue';
import BaseRadio from './Inputs/BaseRadio.vue';
import BaseInput from './Inputs/BaseInput.vue';
import Badge from './Badge';
import BaseProgress from './BaseProgress.vue';
import BaseButton from './BaseButton.vue';
import BaseCheckbox from './Inputs/BaseCheckbox.vue'
import BaseAlert from './BaseAlert.vue'
import BaseRadio from './Inputs/BaseRadio.vue'
import BaseInput from './Inputs/BaseInput.vue'
import Badge from './Badge'
import BaseProgress from './BaseProgress.vue'
import BaseButton from './BaseButton.vue'
import BaseDropdown from './BaseDropdown.vue';
import BaseTable from './BaseTable.vue';
import BaseDropdown from './BaseDropdown.vue'
import BaseTable from './BaseTable.vue'
import Card from './Cards/Card.vue';
import StatsCard from './Cards/StatsCard.vue';
import BaseNav from './Navbar/BaseNav';
import NavbarToggleButton from './Navbar/NavbarToggleButton';
import Card from './Cards/Card.vue'
import StatsCard from './Cards/StatsCard.vue'
import BaseNav from './Navbar/BaseNav'
import NavbarToggleButton from './Navbar/NavbarToggleButton'
import TabPane from './Tabs/Tab.vue';
import Tabs from './Tabs/Tabs.vue';
import Collapse from './Collapse/Collapse.vue';
import CollapseItem from './Collapse/CollapseItem.vue';
import Modal from './Modal.vue';
import BaseSlider from './BaseSlider.vue';
import LoadingPanel from './LoadingPanel.vue';
import TabPane from './Tabs/Tab.vue'
import Tabs from './Tabs/Tabs.vue'
import Collapse from './Collapse/Collapse.vue'
import CollapseItem from './Collapse/CollapseItem.vue'
import Modal from './Modal.vue'
import BaseSlider from './BaseSlider.vue'
import LoadingPanel from './LoadingPanel.vue'
import BasePagination from './BasePagination.vue';
import BasePagination from './BasePagination.vue'
import SidebarPlugin from './SidebarPlugin';
import SidebarPlugin from './SidebarPlugin'
export {
BaseCheckbox,
@ -48,5 +48,5 @@ export {
BaseButton,
Collapse,
CollapseItem,
LoadingPanel
};
LoadingPanel,
}

View File

@ -11,8 +11,12 @@ const environment = {
const server = {
LOGIN_API_URL: process.env.LOGIN_API_URL || 'http://localhost/login_api/',
COMMUNITY_API_STATE_BALANCE_URL: process.env.COMMUNITY_API_STATE_BALANCE_URL || 'http://localhost/state-balances/',
COMMUNITY_API_TRANSACTION_CREATION_URL: process.env.COMMUNITY_API_TRANSACTION_CREATION_URL || 'http://localhost/transaction-creations/',
COMMUNITY_API_STATE_BALANCE_URL:
process.env.COMMUNITY_API_STATE_BALANCE_URL || 'http://localhost/state-balances/',
// Schöpfung
// COMMUNITY_API_TRANSACTION_CREATION_URL: process.env.COMMUNITY_API_TRANSACTION_CREATION_URL || 'http://localhost/transaction-creations/',
COMMUNITY_API_TRANSACTION_SEND_COINS:
process.env.COMMUNITY_API_TRANSACTION_SEND_COINS || 'http://localhost/transaction-send-coins/',
}
const CONFIG = {

View File

@ -1,15 +1,15 @@
export default {
bind: function(el, binding, vnode) {
el.clickOutsideEvent = function(event) {
bind: function (el, binding, vnode) {
el.clickOutsideEvent = function (event) {
// here I check that click was outside the el and his childrens
if (!(el == event.target || el.contains(event.target))) {
// and if it did, call method provided in attribute value
vnode.context[binding.expression](event);
vnode.context[binding.expression](event)
}
};
document.body.addEventListener('click', el.clickOutsideEvent);
}
document.body.addEventListener('click', el.clickOutsideEvent)
},
unbind: function(el) {
document.body.removeEventListener('click', el.clickOutsideEvent);
}
};
unbind: function (el) {
document.body.removeEventListener('click', el.clickOutsideEvent)
},
}

View File

@ -1,16 +1,31 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import en from 'vee-validate/dist/locale/en'
import de from 'vee-validate/dist/locale/de'
Vue.use(VueI18n)
function loadLocaleMessages () {
function loadLocaleMessages() {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
const messages = {}
locales.keys().forEach(key => {
locales.keys().forEach((key) => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
messages[locale] = locales(key)
if (locale === 'de') {
messages[locale] = {
validations: de,
...messages[locale],
}
}
if (locale === 'en') {
messages[locale] = {
validations: en,
...messages[locale],
}
}
}
})
return messages
@ -19,20 +34,23 @@ function loadLocaleMessages () {
const numberFormats = {
'en-US': {
currency: {
style: 'currency'
}
style: 'currency',
currency: 'GDD',
abbreviate: true,
},
},
'de-DE': {
currency: {
style: 'currency'
}
}
style: 'currency',
currency: 'GDD',
abbreviate: true,
},
},
}
export default new VueI18n({
locale: 'en',
fallbackLocale: 'en',
messages: loadLocaleMessages(),
numberFormats
})
numberFormats,
})

File diff suppressed because one or more lines are too long

View File

@ -1,21 +1,21 @@
{
"message": "hallo gradido !!",
"welcome":"Willkommen!",
"logout":"Logout",
"logout":"Abmelden",
"login":"Login",
"signup": "Registrieren",
"imprint":"Impressum",
"privacy_policy":"Datenschutzerklärung",
"license":"Lizenz",
"back":"zurück",
"back":"Zurück",
"sent":"Senden",
"transactions":"Transaktionen",
"language":"Sprachen",
"language":"Language",
"languages":{
"de": "Deutsch",
"en": "English"
},
"form": {
"cancel":"abbrechen",
"cancel":"Abbrechen",
"firstname":"Vorname",
"lastname":"Nachname",
"email":"eMail",
@ -29,7 +29,7 @@
"from":"von",
"to":"bis",
"at":"am",
"send_now":"jetzt versenden"
"send_now":"Jetzt versenden"
},
"transaction":{
"show_part": "Die letzten <strong>{count}</strong> Transaktionen",
@ -38,23 +38,23 @@
},
"site": {
"login": {
"community":"Du bist die Gemeinschaft",
"community":"Tausend Dank, weil du bei uns bist!",
"remember":"Passwort merken",
"signin":"Anmelden",
"forgot_pwd":"Passwort vergessen?",
"new_wallet":"Neues Wallet erstellen"
"new_wallet":"Neues Konto erstellen"
},
"signup": {
"title": "Erstelle deine Gradido Wallet",
"subtitle": "Werde ein teil der Gemeinschaft!",
"title": "Erstelle dein Gradido-Konto",
"subtitle": "Werde Teil der Gemeinschaft!",
"strength":"Passwortsicherheit:",
"strong":"stark",
"agree":"habe ich gelesen und verstanden und stimme diesen zu."
},
"password": {
"title": "Passwort zurücksetzen",
"subtitle": "Wenn sie das Passwort vergessen haben oder ändern möchten.",
"reset_now": "jetzt zurücksetzen"
"subtitle": "Wenn du dein Passwort vergessen hast, kannst du es hier zurücksetzen.",
"reset_now": "Jetzt zurücksetzen"
},
"thx": {
"title": "Danke!",
@ -68,7 +68,7 @@
},
"navbar" : {
"my-profil":"Mein Profil",
"settings":"Einstellung",
"settings":"Einstellungen",
"activity":"Aktivität",
"support":"Support"
},
@ -80,19 +80,19 @@
},
"landing1" : {
"explore": "Erkunden Sie Gradido",
"text": "Gesundes Geld für eine gesunde Welt - Das Gradido-Modell kann weltweiten Wohlstand und Frieden schaffen",
"text": "Gesundes Geld für eine gesunde Welt Das Gradido-Modell kann weltweiten Wohlstand und Frieden schaffen",
"link": "Seiten erkunden"
},
"404" : {
"ooops" : "Ooops!",
"text" : "Seite nicht gefunden. Aber keine Sorge, wir haben noch viele andere Seiten zum Erkunden",
"back" : "zurück zur Übersicht!"
"back" : "Zurück zur Übersicht!"
}
},
"admin": {
"site": {
"overview": {
"created": "created",
"created": "Schöpfen",
"transience" : "Transience",
"exchanged": "Exchanged",
"members" : "Members"
@ -101,9 +101,5 @@
},
"nav": {
"features": "Neuigkeiten"
},
"about": {
"title": "Über uns",
"content": "Lorem ipsum deutsch dolor sit amet consectetur adipisicing elit. Aut dicta incidunt ea ut commodi quidem temporibus illo quia. Et itaque deleniti veniam tempore facere ipsum animi totam culpa minima vel voluptatem adipisci natus blanditiis similique sunt expedita, ex dicta doloremque repellat vitae temporibus. Quisquam quia, accusantium blanditiis architecto facilis saepe! Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad unde laborum ut suscipit iure aliquam sapiente doloribus exercitationem nam sint."
}
}
}

View File

@ -4,24 +4,24 @@
"logout":"Logout",
"login":"Login",
"signup": "Sign up",
"imprint":"Imprint",
"imprint":"Legal notice",
"privacy_policy":"Privacy policy",
"license":"License",
"back":"back",
"transactions":"transactions",
"language":"Language",
"back":"Back",
"sent":"Send",
"transactions":"Transactions",
"language":"Sprache",
"languages":{
"de": "Deutsch",
"en": "English"
"de": "Deutsch",
"en": "English"
},
"form": {
"cancel":"Cancel",
"firstname":"Firstname",
"lastname":"Lastname",
"email":"Email",
"email_repeat":"repeat_email",
"email_repeat":"Repeat Email",
"password":"Password",
"password_repeat":"repeat password",
"password_repeat":"Repeat password",
"amount":"Amount",
"memo":"Memo",
"message":"Message",
@ -30,7 +30,7 @@
"to":"to",
"at":"at",
"time":"Time",
"send_now":"send now"
"send_now":"Send now"
},
"transaction":{
"show_part": "The last <strong>{count}</strong> transactions",
@ -39,27 +39,27 @@
},
"site": {
"login": {
"community":"You are the community",
"remember":"Remember me",
"community":"A thousand thanks for being with us!",
"remember":"Remember password",
"signin":"Sign in",
"forgot_pwd":"Forgot password?",
"new_wallet":"Create new wallet"
"new_wallet":"Create new account"
},
"signup": {
"title": "Create your Gradido Wallet",
"title": "Create your Gradido account",
"subtitle": "Become a part of the community!",
"strength":"password strength:",
"strength":"Password strength:",
"strong":"strong",
"agree":"I have read and understood and agree to them the"
"agree":"I have read and understood and agree to them"
},
"password": {
"title": "reset password",
"subtitle": "If you have forgotten the password or want to change it.",
"reset_now": "reset now"
"title": "Reset password",
"subtitle": "If you have forgotten your password, you can reset it here.",
"reset_now": "Reset now"
},
"thx": {
"title": "Thank you",
"subtitle": "We have sent you an eMail."
"title": "Thank you!",
"subtitle": "We have sent you an email."
},
"overview":{
"account_overview":"Account overview",
@ -68,42 +68,39 @@
"add_work":"New Community Contribution"
},
"navbar" : {
"my-profil":"My Profil",
"my-profil":"My profile",
"settings":"Settings",
"activity":"Activity",
"support":"Support"
},
"sidebar" : {
"community":"Community",
"members_area":"Members area",
"membership":"Membership"
"community":"Community",
"members_area":"Members area",
"membership":"Membership",
"language":"Languages"
},
"landing1" : {
"explore":"Explore Gradido",
"text":"If you want to get inspiration or just show something directly to your clients, you can jump start your development with our pre-built example pages.",
"link":"Explore pages"
"explore":"Explore Gradido",
"text":"Healthy money for a healthy world — the Gradido model can create global prosperity and peace",
"link":"Explore pages"
},
"404" : {
"ooops" : "Ooops!",
"text" : "Page not found. Don't worry though, we have plenty of other pages to explore",
"back" : "Back to dashboard!"
"ooops" : "Ooops!",
"text" : "Page not found. Do not worry though, we have plenty of other pages to explore",
"back" : "Back to dashboard!"
}
},
"admin": {
"site": {
"overview": {
"creation": "Creation",
"transience" : "Transience",
"exchanged": "Exchanged",
"members" : "Members"
},
"admin": {
"site": {
"overview": {
"creation": "Creation",
"transience" : "Transience",
"exchanged": "Exchanged",
"members" : "Members"
}
}
}
},
},
"nav": {
"features": "Features"
},
"about": {
"title": "About",
"content": "Lorem ipsum english dolor sit amet consectetur adipisicing elit. Aut dicta incidunt ea ut commodi quidem temporibus illo quia. Et itaque deleniti veniam tempore facere ipsum animi totam culpa minima vel voluptatem adipisci natus blanditiis similique sunt expedita, ex dicta doloremque repellat vitae temporibus. Quisquam quia, accusantium blanditiis architecto facilis saepe! Lorem ipsum dolor sit amet consectetur adipisicing elit. Ad unde laborum ut suscipit iure aliquam sapiente doloribus exercitationem nam sint."
}
}
}

View File

@ -1,20 +1,20 @@
import Vue from 'vue';
import DashboardPlugin from './plugins/dashboard-plugin';
import App from './App.vue';
import i18n from './i18n.js';
import VueCookies from 'vue-cookies';
import Vue from 'vue'
import DashboardPlugin from './plugins/dashboard-plugin'
import App from './App.vue'
import i18n from './i18n.js'
import VeeValidate from './vee-validate.js'
import VueCookies from 'vue-cookies'
// store
import {store} from './store/store';
import { store } from './store/store'
// router setup
import router from './routes/router';
import router from './routes/router'
// plugin setup
Vue.use(DashboardPlugin);
Vue.config.productionTip = false;
Vue.use(VueCookies);
Vue.use(DashboardPlugin)
Vue.config.productionTip = false
Vue.use(VueCookies)
/* eslint-disable no-new */
new Vue({
@ -22,5 +22,5 @@ new Vue({
router,
store,
i18n,
render: h => h(App)
});
render: (h) => h(App),
})

View File

@ -1,67 +1,67 @@
// Polyfills for js features used in the Dashboard but not supported in some browsers (mainly IE)
import '@/polyfills';
import '@/polyfills'
// Notifications plugin. Used on Notifications page
import Notifications from '@/components/NotificationPlugin';
import Notifications from '@/components/NotificationPlugin'
// Validation plugin used to validate forms
import { configure } from 'vee-validate';
import { configure } from 'vee-validate'
// A plugin file where you could register global components used across the app
import GlobalComponents from './globalComponents';
import GlobalComponents from './globalComponents'
// A plugin file where you could register global directives
import GlobalDirectives from './globalDirectives';
import GlobalDirectives from './globalDirectives'
// Sidebar on the right. Used as a local plugin in DashboardLayout.vue
import SideBar from '@/components/SidebarPlugin';
import SideBar from '@/components/SidebarPlugin'
// element ui language configuration
import lang from 'element-ui/lib/locale/lang/en';
import locale from 'element-ui/lib/locale';
locale.use(lang);
import lang from 'element-ui/lib/locale/lang/en'
import locale from 'element-ui/lib/locale'
locale.use(lang)
// vue-bootstrap
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
// asset imports
import '@/assets/scss/argon.scss';
import '@/assets/vendor/nucleo/css/nucleo.css';
import { extend } from 'vee-validate';
import * as rules from 'vee-validate/dist/rules';
import { messages } from 'vee-validate/dist/locale/en.json';
import '@/assets/scss/argon.scss'
import '@/assets/vendor/nucleo/css/nucleo.css'
import { extend } from 'vee-validate'
import * as rules from 'vee-validate/dist/rules'
import { messages } from 'vee-validate/dist/locale/en.json'
import VueQrcodeReader from "vue-qrcode-reader";
import VueQrcode from "vue-qrcode";
import VueQrcodeReader from 'vue-qrcode-reader'
import VueQrcode from 'vue-qrcode'
import VueFlatPickr from 'vue-flatpickr-component'
import VueGoodTablePlugin from 'vue-good-table';
import VueGoodTablePlugin from 'vue-good-table'
// import the styles
import 'vue-good-table/dist/vue-good-table.css';
import 'vue-good-table/dist/vue-good-table.css'
import VueMoment from 'vue-moment';
import VueMoment from 'vue-moment'
Object.keys(rules).forEach(rule => {
Object.keys(rules).forEach((rule) => {
extend(rule, {
...rules[rule], // copies rule configuration
message: messages[rule] // assign message
});
});
message: messages[rule], // assign message
})
})
export default {
install(Vue) {
Vue.use(GlobalComponents);
Vue.use(GlobalDirectives);
Vue.use(SideBar);
Vue.use(Notifications);
Vue.use(BootstrapVue);
Vue.use(IconsPlugin);
Vue.use(VueGoodTablePlugin);
Vue.use(VueMoment);
Vue.use(VueQrcodeReader);
Vue.use(VueQrcode);
Vue.use(GlobalComponents)
Vue.use(GlobalDirectives)
Vue.use(SideBar)
Vue.use(Notifications)
Vue.use(BootstrapVue)
Vue.use(IconsPlugin)
Vue.use(VueGoodTablePlugin)
Vue.use(VueMoment)
Vue.use(VueQrcodeReader)
Vue.use(VueQrcode)
Vue.use(VueFlatPickr)
configure({
classes: {
valid: 'is-valid',
invalid: 'is-invalid',
dirty: ['is-dirty', 'is-dirty'], // multiple classes per flag!
}
},
})
}
};
},
}

View File

@ -1,45 +1,45 @@
import BaseInput from '@/components/Inputs/BaseInput.vue';
import BaseDropdown from '@/components/BaseDropdown.vue';
import Card from '@/components/Cards/Card.vue';
import Modal from '@/components/Modal.vue';
import StatsCard from '@/components/Cards/StatsCard.vue';
import BaseButton from '@/components/BaseButton.vue';
import Badge from '@/components/Badge.vue';
import BaseCheckbox from '@/components/Inputs/BaseCheckbox.vue';
import BaseRadio from "@/components/Inputs/BaseRadio";
import BaseProgress from "@/components/BaseProgress";
import BasePagination from "@/components/BasePagination";
import BaseAlert from "@/components/BaseAlert";
import BaseNav from "@/components/Navbar/BaseNav";
import BaseHeader from '@/components/BaseHeader';
import { ValidationProvider, ValidationObserver } from 'vee-validate';
import { Input, Tooltip, Popover } from 'element-ui';
import BaseInput from '@/components/Inputs/BaseInput.vue'
import BaseDropdown from '@/components/BaseDropdown.vue'
import Card from '@/components/Cards/Card.vue'
import Modal from '@/components/Modal.vue'
import StatsCard from '@/components/Cards/StatsCard.vue'
import BaseButton from '@/components/BaseButton.vue'
import Badge from '@/components/Badge.vue'
import BaseCheckbox from '@/components/Inputs/BaseCheckbox.vue'
import BaseRadio from '@/components/Inputs/BaseRadio'
import BaseProgress from '@/components/BaseProgress'
import BasePagination from '@/components/BasePagination'
import BaseAlert from '@/components/BaseAlert'
import BaseNav from '@/components/Navbar/BaseNav'
import BaseHeader from '@/components/BaseHeader'
import { ValidationProvider, ValidationObserver } from 'vee-validate'
import { Input, Tooltip, Popover } from 'element-ui'
/**
* You can register global components here and use them as a plugin in your main Vue instance
*/
const GlobalComponents = {
install(Vue) {
Vue.component(Badge.name, Badge);
Vue.component(BaseAlert.name, BaseAlert);
Vue.component(BaseButton.name, BaseButton);
Vue.component(BaseCheckbox.name, BaseCheckbox);
Vue.component(BaseHeader.name, BaseHeader);
Vue.component(BaseInput.name, BaseInput);
Vue.component(BaseDropdown.name, BaseDropdown);
Vue.component(BaseNav.name, BaseNav);
Vue.component(BasePagination.name, BasePagination);
Vue.component(BaseProgress.name, BaseProgress);
Vue.component(BaseRadio.name, BaseRadio);
Vue.component(Card.name, Card);
Vue.component(Modal.name, Modal);
Vue.component(StatsCard.name, StatsCard);
Vue.component(Input.name, Input);
Vue.component(Badge.name, Badge)
Vue.component(BaseAlert.name, BaseAlert)
Vue.component(BaseButton.name, BaseButton)
Vue.component(BaseCheckbox.name, BaseCheckbox)
Vue.component(BaseHeader.name, BaseHeader)
Vue.component(BaseInput.name, BaseInput)
Vue.component(BaseDropdown.name, BaseDropdown)
Vue.component(BaseNav.name, BaseNav)
Vue.component(BasePagination.name, BasePagination)
Vue.component(BaseProgress.name, BaseProgress)
Vue.component(BaseRadio.name, BaseRadio)
Vue.component(Card.name, Card)
Vue.component(Modal.name, Modal)
Vue.component(StatsCard.name, StatsCard)
Vue.component(Input.name, Input)
Vue.component('validation-provider', ValidationProvider)
Vue.component('validation-observer', ValidationObserver)
Vue.use(Tooltip);
Vue.use(Popover);
}
};
Vue.use(Tooltip)
Vue.use(Popover)
},
}
export default GlobalComponents;
export default GlobalComponents

View File

@ -1,4 +1,4 @@
import clickOutside from '@/directives/click-ouside.js';
import clickOutside from '@/directives/click-ouside.js'
/**
* You can register global directives here and use them as a plugin in your main Vue instance
@ -6,8 +6,8 @@ import clickOutside from '@/directives/click-ouside.js';
const GlobalDirectives = {
install(Vue) {
Vue.directive('click-outside', clickOutside);
}
};
Vue.directive('click-outside', clickOutside)
},
}
export default GlobalDirectives;
export default GlobalDirectives

View File

@ -8,24 +8,23 @@ Vue.use(VueRouter)
const router = new VueRouter({
routes, // short for routes: routes
linkActiveClass: 'active',
scrollBehavior: (to, from ,savedPosition) => {
scrollBehavior: (to, from, savedPosition) => {
if (savedPosition) {
return savedPosition;
return savedPosition
}
if (to.hash) {
return { selector: to.hash }
}
return { x: 0, y: 0 }
}
});
router.beforeEach((to, from, next) => {
let language = to.params.lang
if (!language) {
language = 'de'
}
next()
},
})
export default router
router.beforeEach((to, from, next) => {
let language = to.params.lang
if (!language) {
language = 'de'
}
next()
})
export default router

View File

@ -2,8 +2,7 @@ import DashboardLayout from '@/views/Layout/DashboardLayout.vue'
import AuthLayoutGDD from '@/views/Layout/AuthLayout_gdd.vue'
import AuthLayout from '@/views/Layout/AuthLayout.vue'
import NotFound from '@/views/NotFoundPage.vue'
// import NotFound from '@/views/NotFoundPage.vue'
const routes = [
{
@ -13,47 +12,56 @@ const routes = [
children: [
{
path: '/login',
component: () => import('../views/Pages/Login.vue')
component: () => import('../views/Pages/Login.vue'),
},
{
path: '/thx',
component: () => import('../views/Pages/thx.vue')
},
component: () => import('../views/Pages/thx.vue'),
},
{
path: '/register',
component: () => import('../views/Pages/Register.vue')
},
component: () => import('../views/Pages/Register.vue'),
},
{
path: '/password',
component: () => import('../views/Pages/Password.vue')
}
]
component: () => import('../views/Pages/Password.vue'),
},
{
path: '/explorer',
name: 'Explorer',
component: () => import('../views/Pages/Explorer.vue'),
},
],
},
{
path: '/',
redirect: 'KontoOverview',
redirect: 'overview',
component: DashboardLayout,
children: [
{
path: '/KontoOverview',
path: '/overview',
component: () => import('../views/KontoOverview.vue'),
meta: {
requiresAuth: true
}
},
requiresAuth: true,
},
},
{
path: '/profile',
component: () => import('../views/Pages/UserProfileCard.vue')
},
component: () => import('../views/Pages/UserProfileCard.vue'),
},
{
path: '/profileedit',
component: () => import('../views/Pages/UserProfileEdit.vue')
},
component: () => import('../views/Pages/UserProfileEdit.vue'),
},
{
path: '/activity',
component: () => import('../views/Pages/UserProfileActivity.vue')
}
]
component: () => import('../views/Pages/UserProfileActivity.vue'),
},
{
path: '/transactions',
component: () => import('../views/Pages/UserProfileTransactionList.vue'),
},
],
},
,
{
@ -66,20 +74,11 @@ const routes = [
name: 'Adminübersicht',
component: () => import('../views/AdminOverview.vue'),
meta: {
requiresAuth: true
}
}
]
requiresAuth: true,
},
},
],
},
{
path: '/',
redirect: 'login',
component: AuthLayout,
children: [
{ path: '*', component: NotFound }
]
}
];
]
export default routes;
export default routes

View File

@ -1,9 +1,9 @@
import Vue from 'vue';
import Router from 'vue-router';
import DashboardLayout from '../views/Starter/SampleLayout.vue';
import Starter from '../views/Starter/SamplePage.vue';
import Vue from 'vue'
import Router from 'vue-router'
import DashboardLayout from '../views/Starter/SampleLayout.vue'
import Starter from '../views/Starter/SamplePage.vue'
Vue.use(Router);
Vue.use(Router)
export default new Router({
routes: [
@ -16,18 +16,18 @@ export default new Router({
{
path: 'dashboard',
name: 'dashboard',
components: { default: Starter }
}
]
}
components: { default: Starter },
},
],
},
],
scrollBehavior: (to, from ,savedPosition) => {
scrollBehavior: (to, from, savedPosition) => {
if (savedPosition) {
return savedPosition;
return savedPosition
}
if (to.hash) {
return { selector: to.hash };
return { selector: to.hash }
}
return { x: 0, y: 0 };
}
});
return { x: 0, y: 0 }
},
})

View File

@ -4,30 +4,38 @@ Vue.use(Vuex)
import router from '../routes/router.js'
import loginAPI from '../apis/loginAPI'
import communityAPI from '../apis/communityAPI'
import axios from 'axios'
export const store = new Vuex.Store({
state: {
session_id: null,
email: null,
user : {
name:"",
language: 'en',
sizeDE: 'normal',
sizeGB: 'big',
loginfail: false,
user: {
name: '',
balance: 0,
balance_gdt: 0
balance_gdt: 0,
},
ajaxCreateData: {
session_id : '',
email: "",
ajaxCreateData: {
session_id: '',
email: '',
amount: 0,
target_date:"2021-02-19T13:25:36+00:00",
memo:"",
auto_sign: true
target_date: '2021-02-19T13:25:36+00:00',
memo: '',
auto_sign: true,
},
transactions: [],
modals: false
modals: false,
optionAxios: {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true',
},
},
},
// Retrieve a state variable
getters: {
//isLoggedIn: (state /*, getters */) => {
// return state.session_id !== null;
@ -35,107 +43,128 @@ export const store = new Vuex.Store({
},
// Syncronous mutation of the state
mutations: {
language: (state, language) => {
//console.log('mutation: language', language)
state.language = language
$cookies.set('gdd_lang', language)
if (state.language == 'de') {
state.sizeDE = 'big'
state.sizeGB = 'normal'
} else {
state.sizeDE = 'normal'
state.sizeGB = 'big'
}
},
loginfail: (state, loginfail) => {
//console.log('mutation: email')
state.loginfail = loginfail
},
email: (state, email) => {
//console.log('mutation: email')
state.email = email
},
session_id: (state,session_id) => {
session_id: (state, session_id) => {
//console.log('mutation: session_id')
state.session_id = session_id
},
user_balance: (state,balance) => {
user_balance: (state, balance) => {
//console.log('mutation: user_balance')
state.user.balance = (balance)/10000
state.user.balance = balance / 10000
},
user_balance_gdt: (state,balance) => {
user_balance_gdt: (state, balance) => {
//console.log('mutation: user_balance_gdt')
state.user.balance_gdt = (balance)/10000
state.user.balance_gdt = balance / 10000
},
transactions: (state,transactions) => {
transactions: (state, transactions) => {
//console.log('mutation: transactions')
state.transactions = transactions
}
},
},
// Asyncronous actions - used for api calls
actions: {
login: async ({ dispatch, commit }, data) => {
console.log('action: login')
const result = await loginAPI.login(data.email,data.password)
console.log('result',result)
console.log('result.success',result.success)
if( result.success ){
const result = await loginAPI.login(data.email, data.password)
if (result.success) {
commit('session_id', result.result.data.session_id)
commit('email', data.email)
$cookies.set('gdd_session_id', result.result.data.session_id);
$cookies.set('gdd_u', data.email);
router.push('/KontoOverview')
$cookies.set('gdd_session_id', result.result.data.session_id)
$cookies.set('gdd_u', data.email)
router.push('/overview')
} else {
// Register failed, we perform a logout
dispatch('logout')
}
//alert('>>>>> FAIl LOGIN')
commit('loginfail', true)
//dispatch('logout')
}
},
passwordReset: async (data) => {
console.log("<<<<<<<<<<< PASSWORT RESET TODO >>>>>>>>>>>", data.email)
//console.log('<<<<<<<<<<< PASSWORT RESET TODO >>>>>>>>>>>', data.email)
},
schoepfen: async (data) => {
// http://localhost/transaction-creations/ajaxCreate
// http://localhost/transaction-creations/ajaxCreate
},
createUser: async ({ commit, dispatch }, data) => {
console.log('action: createUser')
const result = await loginAPI.create(data.email,data.first_name,data.last_name,data.password)
if( result.success ){
// console.log('action: createUser')
const result = await loginAPI.create(
data.email,
data.first_name,
data.last_name,
data.password,
)
if (result.success) {
commit('session_id', result.result.data.session_id)
commit('email', data.email)
$cookies.set('gdd_session_id', result.result.data.session_id);
$cookies.set('gdd_u', data.email);
router.push('/KontoOverview')
$cookies.set('gdd_session_id', result.result.data.session_id)
$cookies.set('gdd_u', data.email)
router.push('/overview')
} else {
// Register failed, we perform a logout
// console.log('action createUser to logout start')
dispatch('logout')
}
},
logout: async ({ commit , state }) => {
console.log('action: logout')
},
logout: async ({ commit, state }) => {
//console.log('action: logout')
// Are we actually logged in?
if(state.session_id){
if (state.session_id) {
const result = await loginAPI.logout(state.session_id)
// The result can be error, but thats ok with us
}
commit('session_id', null)
commit('email', null)
$cookies.remove('gdd_session_id');
$cookies.remove('gdd_u');
commit('email', null)
$cookies.remove('gdd_session_id')
$cookies.remove('gdd_u')
$cookies.remove('gdd_lang')
router.push('/Login')
},
ajaxCreate: async ({ dispatch, state }) => {
//console.log('action: ajaxCreate')
state.ajaxCreateData.amount = (state.ajaxCreateData.amount)*10000
axios.post("http://localhost/transaction-send-coins/ajaxCreate", state.ajaxCreateData).then((result) => {
console.log("store ajaxCreate result", result)
if( result.success ){
// TODO
} else {
//dispatch('logout')
}
}, (error) => {
console.log(error);
});
state.ajaxCreateData.amount = state.ajaxCreateData.amount * 10000
const result = await communityAPI.create($cookies.get('gdd_session_id', email, amount, memo))
//console.log(result)
},
ajaxListTransactions: async ({commit, dispatch, state}) => {
// console.log('action: ajaxListTransactions', state.session_id)
// const result = await communityAPI.transactions(state.session_id)
ajaxListTransactions: async ({ commit, dispatch, state }) => {
// console.log('action: ajaxListTransactions', state.session_id)
// const result = await communityAPI.transactions(state.session_id)
},
accountBalance: async ({ commit, dispatch, state }) => {
//console.log('action: accountBalance')
const result = await communityAPI.balance(state.session_id)
//console.log(result)
if(result.success) {
// console.log('action: dispatch', dispatch)
// console.log('action: state.session_id', state.session_id)
// console.log(" action: $cookies.get('gdd_session_id') ", $cookies.get("gdd_session_id") )
// commit('session_id', $cookies.get("gdd_session_id"))
// commit('email', $cookies.get("gdd_u"))
const result = await communityAPI.balance($cookies.get('gdd_session_id'))
//console.log('accountBalance result', result)
//console.log('aresult.result.data.balance', result.result.data.balance)
if (result.success) {
commit('user_balance', result.result.data.balance)
} else {
//console.log('action accountBalance to logout start')
dispatch('logout')
}
}
}
})
},
},
})

View File

@ -4,12 +4,12 @@
* @param [timeout] the throttle interval
*/
export function throttle(handlerFunc, timeout = 66) {
let resizeTimeout;
let resizeTimeout
if (!resizeTimeout) {
resizeTimeout = setTimeout(() => {
resizeTimeout = null;
handlerFunc();
resizeTimeout = null
handlerFunc()
// The actualResizeHandler will execute at a rate of 15fps
}, timeout);
}, timeout)
}
}

View File

@ -0,0 +1,25 @@
import { configure, extend } from 'vee-validate'
import { required, email, min } from 'vee-validate/dist/rules'
import i18n from './i18n'
configure({
defaultMessage: (field, values) => {
values._field_ = i18n.t(`fields.${field}`)
return i18n.t(`validations.messages.${values._rule_}`, values)
},
})
extend('email', {
...email,
message: (_, values) => i18n.t('validations.messages.email', values),
})
extend('required', {
...required,
message: (_, values) => i18n.t('validations.messages.required', values),
})
extend('min', {
...min,
message: (_, values) => i18n.t('validations.messages.min', values),
})

View File

@ -3,60 +3,72 @@
<base-header class="pb-6 pb-8 pt-5 pt-md-8">
<b-row>
<b-col xl="3" md="6">
<stats-card :title="$t('admin.site.overview.creation')"
type="gradient-red"
sub-title="350,897"
icon="ni ni-active-40"
class="mb-4">
<template slot="footer" >
<stats-card
:title="$t('admin.site.overview.creation')"
type="gradient-red"
sub-title="350,897"
icon="ni ni-active-40"
class="mb-4"
>
<template slot="footer">
<span class="text-success mr-2">3.48%</span>
<span class="text-nowrap">{{ $t('site.overview.since_last_month') }}</span>
<span class="text-nowrap">
{{ $t('site.overview.since_last_month') }}
</span>
</template>
</stats-card>
</b-col>
<b-col xl="3" md="6">
<stats-card :title="$t('admin.site.overview.transience')"
type="gradient-orange"
sub-title="2,356"
icon="ni ni-chart-pie-35"
class="mb-4">
<stats-card
:title="$t('admin.site.overview.transience')"
type="gradient-orange"
sub-title="2,356"
icon="ni ni-chart-pie-35"
class="mb-4"
>
<template slot="footer">
<span class="text-success mr-2">12.18%</span>
<span class="text-nowrap">{{ $t('site.overview.since_last_month') }}</span>
<span class="text-nowrap">
{{ $t('site.overview.since_last_month') }}
</span>
</template>
</stats-card>
</b-col>
<b-col xl="3" md="6">
<stats-card :title="$t('admin.site.overview.exchanged')"
type="gradient-green"
sub-title="924"
icon="ni ni-money-coins"
class="mb-4">
<stats-card
:title="$t('admin.site.overview.exchanged')"
type="gradient-green"
sub-title="924"
icon="ni ni-money-coins"
class="mb-4"
>
<template slot="footer">
<span class="text-danger mr-2">5.72%</span>
<span class="text-nowrap">{{ $t('site.overview.since_last_month') }}</span>
<span class="text-nowrap">
{{ $t('site.overview.since_last_month') }}
</span>
</template>
</stats-card>
</b-col>
<b-col xl="3" md="6">
<stats-card :title="$t('admin.site.overview.members')"
type="gradient-info"
sub-title="49,65%"
icon="ni ni-chart-bar-32"
class="mb-4">
<stats-card
:title="$t('admin.site.overview.members')"
type="gradient-info"
sub-title="49,65%"
icon="ni ni-chart-bar-32"
class="mb-4"
>
<template slot="footer">
<span class="text-success mr-2">54.8%</span>
<span class="text-nowrap">{{ $t('site.overview.since_last_month') }}</span>
<span class="text-nowrap">
{{ $t('site.overview.since_last_month') }}
</span>
</template>
</stats-card>
</b-col>
</b-row>
</base-header>
<!--Charts-->
<b-container fluid class="mt--7">
<b-row>
@ -65,17 +77,18 @@
<b-row align-v="center" slot="header">
<b-col>
<h6 class="text-light text-uppercase ls-1 mb-1">Charts</h6>
<h5 class="h3 text-white mb-0">Geschöpft</h5>
<h5 class="h3 mb-0">Geschöpft</h5>
</b-col>
<b-col>
<b-nav class="nav-pills justify-content-end">
<b-nav-item
class="mr-2 mr-md-0"
:active="bigLineChart.activeIndex === 0"
link-classes="py-2 px-1"
@click.prevent="initBigChart(0)">
<span class="d-none d-md-block">geschöpft</span>
<span class="d-md-none">M</span>
class="mr-2 mr-md-0"
:active="bigLineChart.activeIndex === 0"
link-classes="py-2 px-1"
@click.prevent="initBigChart(0)"
>
<span class="d-none d-md-block">geschöpft</span>
<span class="d-md-none">M</span>
</b-nav-item>
<b-nav-item
link-classes="py-2 px-1"
@ -85,7 +98,7 @@
<span class="d-none d-md-block">geteilt</span>
<span class="d-md-none">W</span>
</b-nav-item>
<b-nav-item
<b-nav-item
link-classes="py-2 px-1"
:active="bigLineChart.activeIndex === 2"
@click.prevent="initBigChart(2)"
@ -93,7 +106,7 @@
<span class="d-none d-md-block">vergangen</span>
<span class="d-md-none">W</span>
</b-nav-item>
<b-nav-item
<b-nav-item
link-classes="py-2 px-1"
:active="bigLineChart.activeIndex === 3"
@click.prevent="initBigChart(3)"
@ -109,8 +122,7 @@
ref="bigChart"
:chart-data="bigLineChart.chartData"
:extra-options="bigLineChart.extraOptions"
>
</line-chart>
></line-chart>
</card>
</b-col>
@ -123,261 +135,264 @@
</b-col>
</b-row>
<bar-chart
:height="350"
ref="barChart"
:chart-data="redBarChart.chartData"
>
</bar-chart>
<bar-chart :height="350" ref="barChart" :chart-data="redBarChart.chartData"></bar-chart>
</card>
</b-col>
</b-row>
<!-- End charts-->
<br />
<br />
<br />
<br>
<br>
<br>
<!--Tables User Search && New User-->
<!--Tables User Search && New User-->
<admin-user-search />
<br>
<br>
<br>
<admin-user-creation/>
<br />
<br />
<br />
<admin-user-creation />
<br>
<b-card-header>
<hr>
<h3>Admin Participation List</h3>
<small>/public/json-example/admin_card_statistic.json</small>
<p>
{{cardstatistic}}
</p>
<br />
</b-card-header>
<b-card-header>
<hr>
<h3>Admin Charts Statistic</h3>
<small>/public/json-example/admin_charts_statistic.json</small>
<p>
{{chartsstatistic}}
</p>
<b-card-header>
<hr />
<h3>Admin Participation List</h3>
<small>/public/json-example/admin_card_statistic.json</small>
<p>
{{ cardstatistic }}
</p>
</b-card-header>
<b-card-header>
<hr />
<h3>Admin Charts Statistic</h3>
<small>/public/json-example/admin_charts_statistic.json</small>
<p>
{{ chartsstatistic }}
</p>
</b-card-header>
<b-card-header>
<hr />
<h3>Admin Community Statistic</h3>
<small>/public/json-example/admin_community_statistic.json</small>
<p>
{{ communitystatistic }}
</p>
</b-card-header>
<b-card-header>
<hr />
<h3>Admin User List</h3>
<small>/public/json-example/admin_userlist.json</small>
<p>
{{ userlist }}
</p>
</b-card-header>
<b-card-header>
<hr />
<h3>Admin Transaction List</h3>
<small>/public/json-example/admin_transaction_list.json</small>
<p>
{{ transactionlist }}
</p>
</b-card-header>
<b-card-header>
<hr>
<h3>Admin Community Statistic</h3>
<small>/public/json-example/admin_community_statistic.json</small>
<p>
{{communitystatistic}}
</p>
</b-card-header>
<b-card-header>
<hr>
<h3>Admin User List</h3>
<small>/public/json-example/admin_userlist.json</small>
<p>
{{userlist}}
</p>
</b-card-header>
<b-card-header>
<hr>
<h3>Admin Transaction List</h3>
<small>/public/json-example/admin_transaction_list.json</small>
<p>
{{transactionlist}}
</p>
<hr>
</b-card-header>
<b-card-header>
<h3>Admin Transience List</h3>
<small>/public/json-example/admin_transience_list.json</small>
<p>
{{transiencelist}}
</p>
</b-card-header>
<hr />
</b-card-header>
<b-card-header>
<h3>Admin Transience List</h3>
<small>/public/json-example/admin_transience_list.json</small>
<p>
{{ transiencelist }}
</p>
</b-card-header>
<!--End tables-->
</b-container>
</div>
</template>
<script>
// Charts
import * as chartConfigs from '@/components/Charts/config';
import LineChart from '@/components/Charts/LineChart';
import BarChart from '@/components/Charts/BarChart';
// Charts
import * as chartConfigs from '@/components/Charts/config'
import LineChart from '@/components/Charts/LineChart'
import BarChart from '@/components/Charts/BarChart'
// Components
import BaseProgress from '@/components/BaseProgress';
import StatsCard from '@/components/Cards/StatsCard';
import SearchUser from '@/components/SearchUser';
// Components
import BaseProgress from '@/components/BaseProgress'
import StatsCard from '@/components/Cards/StatsCard'
import SearchUser from '@/components/SearchUser'
// Tables
// Tables
import PageVisitsTable from './Dashboard/PageVisitsTable';
import AdminUserSearch from './AdminOverview/AdminUserSearch';
import AdminUserCreation from './AdminOverview/AdminUserCreation';
import axios from 'axios';
import PageVisitsTable from './Dashboard/PageVisitsTable'
import AdminUserSearch from './AdminOverview/AdminUserSearch'
import AdminUserCreation from './AdminOverview/AdminUserCreation'
import axios from 'axios'
export default {
components: {
LineChart,
BarChart,
//BaseProgress,
StatsCard,
//PageVisitsTable,
AdminUserSearch,
AdminUserCreation,
//SearchUser
},
data() {
return {
statisticdata: [],
cardstatistic:[],
chartsstatistic: [],
communitystatistic: [],
userlist: [],
transactionlist: [],
transiencelist: [],
filter: '',
items: [
{ id: 1, first_name: "Mikkel", last_name: "Hansen", age: 54 },
{ id: 2, first_name: "Kasper", last_name: "Hvidt", age: 42 },
{ id: 3, first_name: "Lasse", last_name: "Boesen", age: 39 },
{ id: 4, first_name: "Kasper", last_name: "Hansen", age: 62 },
{ id: 5, first_name: "Mads", last_name: "Mikkelsen", age: 31 },
export default {
components: {
LineChart,
BarChart,
//BaseProgress,
StatsCard,
//PageVisitsTable,
AdminUserSearch,
AdminUserCreation,
//SearchUser
},
data() {
return {
statisticdata: [],
cardstatistic: [],
chartsstatistic: [],
communitystatistic: [],
userlist: [],
transactionlist: [],
transiencelist: [],
filter: '',
items: [
{ id: 1, first_name: 'Mikkel', last_name: 'Hansen', age: 54 },
{ id: 2, first_name: 'Kasper', last_name: 'Hvidt', age: 42 },
{ id: 3, first_name: 'Lasse', last_name: 'Boesen', age: 39 },
{ id: 4, first_name: 'Kasper', last_name: 'Hansen', age: 62 },
{ id: 5, first_name: 'Mads', last_name: 'Mikkelsen', age: 31 },
],
bigLineChart: {
allData: [
[0, 20, 10, 30, 15, 40, 20, 60, 60],
[0, 20, 5, 25, 10, 30, 35, 60, 40],
[0, 2, 5, 7, 10, 30, 15, 9, 10],
[0, 2, 5, 7, 10, 14, 29, 78, 120],
],
bigLineChart: {
allData: [
[0, 20, 10, 30, 15, 40, 20, 60, 60],
[0, 20, 5, 25, 10, 30, 35, 60, 40],
[0, 2, 5, 7, 10, 30, 15, 9, 10],
[0, 2, 5, 7, 10, 14, 29, 78, 120]
],
activeIndex: 0,
chartData: {
datasets: [
{
label: 'Performance',
data: [0, 20, 10, 30, 15, 40, 20, 60, 60],
}
],
labels: ['May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
},
extraOptions: chartConfigs.blueChartOptions,
},
redBarChart: {
chartData: {
labels: ['Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
datasets: [{
label: 'Sales',
data: [25, 20, 30, 22, 17, 29]
}]
},
extraOptions: chartConfigs.blueChartOptions
}
};
},
methods: {
TransienceList() {
axios.get("/json-example/admin_transience_list.json").then((d) => {
//console.log(d);
this.transiencelist = d.data;
}, (error) => {
console.log(error);
});
},
TransactionList() {
axios.get("/json-example/admin_transaction_list.json").then((d) => {
//console.log(d);
this.transactionlist = d.data;
}, (error) => {
console.log(error);
});
},
UserList() {
axios.get("/json-example/admin_userlist.json").then((d) => {
//console.log(d);
this.userlist = d.data;
}, (error) => {
console.log(error);
});
},
CommunityStatistic() {
axios.get("/json-example/admin_community_statistic.json").then((d) => {
//console.log(d);
this.communitystatistic = d.data;
}, (error) => {
console.log(error);
});
},
ChartsStatistic() {
axios.get("/json-example/admin_charts_statistic.json").then((d) => {
//console.log(d);
this.chartsstatistic = d.data;
}, (error) => {
console.log(error);
});
},
CardStatistic() {
axios.get("/json-example/admin_card_statistic.json").then((d) => {
//console.log(d);
this.cardstatistic = d.data;
}, (error) => {
console.log(error);
});
},
StatisticDatas() {
axios.get("/json-example/admin_statisticdatas.json").then((d) => {
//console.log(d);
this.userdata = d.data;
}, (error) => {
console.log(error);
});
},
initBigChart(index) {
let chartData = {
activeIndex: 0,
chartData: {
datasets: [
{
label: 'Performance',
data: this.bigLineChart.allData[index]
}
data: [0, 20, 10, 30, 15, 40, 20, 60, 60],
},
],
labels: ['May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
};
this.bigLineChart.chartData = chartData;
this.bigLineChart.activeIndex = index;
}
},
mounted() {
this.initBigChart(0);
this.TransienceList();
this.TransactionList();
this.UserList();
this.CommunityStatistic();
this.ChartsStatistic();
this.CardStatistic();
},
extraOptions: chartConfigs.blueChartOptions,
},
redBarChart: {
chartData: {
labels: ['Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
datasets: [
{
label: 'Sales',
data: [25, 20, 30, 22, 17, 29],
},
],
},
extraOptions: chartConfigs.blueChartOptions,
},
}
}
},
methods: {
TransienceList() {
axios.get('/json-example/admin_transience_list.json').then(
(d) => {
//console.log(d);
this.transiencelist = d.data
},
(error) => {
//console.log(error)
},
)
},
TransactionList() {
axios.get('/json-example/admin_transaction_list.json').then(
(d) => {
//console.log(d);
this.transactionlist = d.data
},
(error) => {
//console.log(error)
},
)
},
UserList() {
axios.get('/json-example/admin_userlist.json').then(
(d) => {
//console.log(d);
this.userlist = d.data
},
(error) => {
//console.log(error)
},
)
},
CommunityStatistic() {
axios.get('/json-example/admin_community_statistic.json').then(
(d) => {
//console.log(d);
this.communitystatistic = d.data
},
(error) => {
//console.log(error)
},
)
},
ChartsStatistic() {
axios.get('/json-example/admin_charts_statistic.json').then(
(d) => {
//console.log(d);
this.chartsstatistic = d.data
},
(error) => {
//console.log(error)
},
)
},
CardStatistic() {
axios.get('/json-example/admin_card_statistic.json').then(
(d) => {
//console.log(d);
this.cardstatistic = d.data
},
(error) => {
//console.log(error)
},
)
},
StatisticDatas() {
axios.get('/json-example/admin_statisticdatas.json').then(
(d) => {
//console.log(d);
this.userdata = d.data
},
(error) => {
//console.log(error)
},
)
},
initBigChart(index) {
let chartData = {
datasets: [
{
label: 'Performance',
data: this.bigLineChart.allData[index],
},
],
labels: ['May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
}
this.bigLineChart.chartData = chartData
this.bigLineChart.activeIndex = index
},
},
mounted() {
this.initBigChart(0)
this.TransienceList()
this.TransactionList()
this.UserList()
this.CommunityStatistic()
this.ChartsStatistic()
this.CardStatistic()
},
}
</script>
<style>
.el-table .cell{
.el-table .cell {
padding-left: 0px;
padding-right: 0px;
}

View File

@ -1,87 +1,75 @@
<template>
<div>
<b-card-header>Gradido Schüpfung</b-card-header>
<!--Tables-->
<b-row class="mt-5">
<b-col xl="8" class="mb-5 mb-xl-0">
<vue-good-table
:columns="columns"
:rows="rows"
max-height="370px"
:fixed-header="true"
:line-numbers="false"
styleClass="vgt-table"
theme="nocturnal"
@on-row-click="onRowClick"
@on-search="onSearch"
:search-options="{
enabled: true,
skipDiacritics: true,
searchFn: mySearchFn,
placeholder: 'durchsuche die tabelle'
}"
:pagination-options="{
enabled: true,
mode: 'pages'
}"
>
<div slot="table-actions">
Einträge suchen .
</div>
</vue-good-table>
</b-col>
<b-col xl="4" class="mb-5 mb-xl-0">
<b-card body-class="p-0" header-class="border-0">
<template v-slot:header>
<b-row align-v="center">
<b-col>
<h3 class="mb-0"></h3>
</b-col>
<b-col class="text-right">
<a href="#!" class="btn btn-sm btn-primary">speichern</a>
</b-col>
</b-row>
</template>
<b-card-body>
<p>{{ creation.createdAt }} </p>
<p>{{ creation.group }} </p>
<p>{{ creation.id }} </p>
<p>{{ creation.name }} </p>
<p>{{ creation.originalIndex }} </p>
<p>{{ creation.text }} </p>
<p>{{ creation.vgt_id }} </p>
</b-card-body>
</b-card>
</b-col>
</b-row>
<!--End tables-->
<b-row class="mt-5">
<b-col xl="8" class="mb-5 mb-xl-0">
<vue-good-table
:columns="columns"
:rows="rows"
max-height="370px"
:fixed-header="true"
:line-numbers="false"
styleClass="vgt-table"
theme="nocturnal"
@on-row-click="onRowClick"
@on-search="onSearch"
:search-options="{
enabled: true,
skipDiacritics: true,
searchFn: mySearchFn,
placeholder: 'durchsuche die tabelle',
}"
:pagination-options="{
enabled: true,
mode: 'pages',
}"
>
<div slot="table-actions">Einträge suchen .</div>
</vue-good-table>
</b-col>
<b-col xl="4" class="mb-5 mb-xl-0">
<b-card body-class="p-0" header-class="border-0">
<template v-slot:header>
<b-row align-v="center">
<b-col>
<h3 class="mb-0"></h3>
</b-col>
<b-col class="text-right">
<a href="#!" class="btn btn-sm btn-primary">speichern</a>
</b-col>
</b-row>
</template>
<b-card-body>
<p>{{ creation.createdAt }}</p>
<p>{{ creation.group }}</p>
<p>{{ creation.id }}</p>
<p>{{ creation.name }}</p>
<p>{{ creation.originalIndex }}</p>
<p>{{ creation.text }}</p>
<p>{{ creation.vgt_id }}</p>
</b-card-body>
</b-card>
</b-col>
</b-row>
<!--End tables-->
</div>
</template>
<script>
export default {
name: 'admin-user-creation',
data(){
data() {
return {
creation:{
createdAt:"",
group:"",
id:"",
name:"",
originalIndex:"",
text:"",
vgt_id:"",
creation: {
createdAt: '',
group: '',
id: '',
name: '',
originalIndex: '',
text: '',
vgt_id: '',
},
columns: [
{
@ -107,74 +95,253 @@ export default {
},
],
rows: [
{ id:1, createdAt: '2021-01-25', name:"Sellora", group: "gruppe1", text: "aASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:2, createdAt: '2021-01-31', name:"Jane", group: "gruppe14", text: "WQadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:3, createdAt: '2021-01-30', name:"Susan", group: "gruppe31", text: "IaYXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:4, createdAt: '2020-01-11', name:"Chris", group: "gruppe12", text: "TZadhhCYXCda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:5, createdAt: '2020-12-21', name:"Dan", group: "gruppe11", text: "Wad3323hhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:6, createdAt: '2020-12-31', name:"zBohn", group: "gruppe71", text: "Tadh hD da hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:7, createdAt: '2020-12-31', name:"xyTellohn", group: "gruppe31", text: "Fad AD31 hhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:8, createdAt: '2021-01-25', name:"uSellora", group: "gruppe1", text: "aASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:9, createdAt: '2021-01-31', name:"yJane", group: "gruppe14", text: "55 55 55 WQadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:10, createdAt: '2021-01-20', name:"kSusan", group: "gruppe31", text: "IaYXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:11, createdAt: '2021-01-25', name:"qSellora", group: "gruppe12", text: "aASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:12, createdAt: '2021-01-31', name:"fJane", group: "gruppe15", text: "WQadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:13, createdAt: '2021-01-38', name:"aSusan", group: "gruppe1", text: "asssss IaYXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:14, createdAt: '2020-01-11', name:"uChris", group: "gruppe12", text: "TZadhhCYXCda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:15, createdAt: '2020-12-21', name:"tzDan", group: "gruppe11", text: "Wad33 23hhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:16, createdAt: '2020-12-31', name:"fBohn", group: "gruppe71", text: "3f Tadh hD da hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:17, createdAt: '2020-12-31', name:"aEsau", group: "gruppe15", text: "2324 adhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:18, createdAt: '2021-01-25', name:"gSellora", group: "gruppe1", text: "5h aASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:19, createdAt: '2021-01-31', name:"sJane", group: "gruppe14", text: "as sdsd WQadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:20, createdAt: '2021-01-30', name:"oSusan", group: "gruppe31", text: "sd IaYXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:21, createdAt: '2021-01-25', name:"Sellora", group: "gruppe1", text: "3 aASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:22, createdAt: '2021-01-13', name:"rtJane", group: "gruppe14", text: "q WQadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:23, createdAt: '2021-01-16', name:"rSusan", group: "gruppe31", text: "Iare YXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:24, createdAt: '2020-01-15', name:"sChris", group: "gruppe12", text: "43 TZad hhCYXCda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:25, createdAt: '2020-12-14', name:"dDan", group: "gruppe11", text: "aas aWad3323hhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:26, createdAt: '2020-12-13', name:"fBohn", group: "gruppe71", text: "Ta adh hD da hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:27, createdAt: '2020-12-12', name:"gTellohn", group: "gruppe31", text: "55 Fad AD31 hhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:28, createdAt: '2021-01-12', name:"zSellora", group: "gruppe1", text: "e q raASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:29, createdAt: '2021-01-12', name:"uJane", group: "gruppe14", text: "erW QadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
{ id:30, createdAt: '2021-01-11', name:"iSusan", group: "gruppe31", text: "3IaYXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk" },
],
};
{
id: 1,
createdAt: '2021-01-25',
name: 'Sellora',
group: 'gruppe1',
text: 'aASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 2,
createdAt: '2021-01-31',
name: 'Jane',
group: 'gruppe14',
text: 'WQadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 3,
createdAt: '2021-01-30',
name: 'Susan',
group: 'gruppe31',
text: 'IaYXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 4,
createdAt: '2020-01-11',
name: 'Chris',
group: 'gruppe12',
text: 'TZadhhCYXCda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 5,
createdAt: '2020-12-21',
name: 'Dan',
group: 'gruppe11',
text: 'Wad3323hhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 6,
createdAt: '2020-12-31',
name: 'zBohn',
group: 'gruppe71',
text: 'Tadh hD da hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 7,
createdAt: '2020-12-31',
name: 'xyTellohn',
group: 'gruppe31',
text: 'Fad AD31 hhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 8,
createdAt: '2021-01-25',
name: 'uSellora',
group: 'gruppe1',
text: 'aASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 9,
createdAt: '2021-01-31',
name: 'yJane',
group: 'gruppe14',
text: '55 55 55 WQadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 10,
createdAt: '2021-01-20',
name: 'kSusan',
group: 'gruppe31',
text: 'IaYXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 11,
createdAt: '2021-01-25',
name: 'qSellora',
group: 'gruppe12',
text: 'aASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 12,
createdAt: '2021-01-31',
name: 'fJane',
group: 'gruppe15',
text: 'WQadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 13,
createdAt: '2021-01-38',
name: 'aSusan',
group: 'gruppe1',
text: 'asssss IaYXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 14,
createdAt: '2020-01-11',
name: 'uChris',
group: 'gruppe12',
text: 'TZadhhCYXCda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 15,
createdAt: '2020-12-21',
name: 'tzDan',
group: 'gruppe11',
text: 'Wad33 23hhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 16,
createdAt: '2020-12-31',
name: 'fBohn',
group: 'gruppe71',
text: '3f Tadh hD da hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 17,
createdAt: '2020-12-31',
name: 'aEsau',
group: 'gruppe15',
text: '2324 adhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 18,
createdAt: '2021-01-25',
name: 'gSellora',
group: 'gruppe1',
text: '5h aASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 19,
createdAt: '2021-01-31',
name: 'sJane',
group: 'gruppe14',
text: 'as sdsd WQadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 20,
createdAt: '2021-01-30',
name: 'oSusan',
group: 'gruppe31',
text: 'sd IaYXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 21,
createdAt: '2021-01-25',
name: 'Sellora',
group: 'gruppe1',
text: '3 aASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 22,
createdAt: '2021-01-13',
name: 'rtJane',
group: 'gruppe14',
text: 'q WQadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 23,
createdAt: '2021-01-16',
name: 'rSusan',
group: 'gruppe31',
text: 'Iare YXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 24,
createdAt: '2020-01-15',
name: 'sChris',
group: 'gruppe12',
text: '43 TZad hhCYXCda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 25,
createdAt: '2020-12-14',
name: 'dDan',
group: 'gruppe11',
text: 'aas aWad3323hhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 26,
createdAt: '2020-12-13',
name: 'fBohn',
group: 'gruppe71',
text: 'Ta adh hD da hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 27,
createdAt: '2020-12-12',
name: 'gTellohn',
group: 'gruppe31',
text: '55 Fad AD31 hhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 28,
createdAt: '2021-01-12',
name: 'zSellora',
group: 'gruppe1',
text: 'e q raASDASdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 29,
createdAt: '2021-01-12',
name: 'uJane',
group: 'gruppe14',
text: 'erW QadCXhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
{
id: 30,
createdAt: '2021-01-11',
name: 'iSusan',
group: 'gruppe31',
text: '3IaYXCYdhhda hkahkh hakdh kaks hkadhk kdhkasdh kdhkhka dka dhakhdk',
},
],
}
},
methods: {
onRowClick(params) {
console.log("onRowClick", params.row.createdAt)
console.log("onRowClick", params.row.group)
console.log("onRowClick", params.row.id)
console.log("onRowClick", params.row.name)
console.log("onRowClick", params.row.originalIndex)
console.log("onRowClick", params.row.text)
console.log("onRowClick", params.row.vgt_id)
onRowClick(params) {
/*
console.log('onRowClick', params.row.createdAt)
console.log('onRowClick', params.row.group)
console.log('onRowClick', params.row.id)
console.log('onRowClick', params.row.name)
console.log('onRowClick', params.row.originalIndex)
console.log('onRowClick', params.row.text)
console.log('onRowClick', params.row.vgt_id)
*/
this.creation.createdAt = params.row.createdAt
this.creation.group = params.row.group
this.creation.id = params.row.id
this.creation.name = params.row.name
this.creation.originalIndex = params.row.originalIndex
this.creation.text = params.row.text
this.creation.vgt_id = params.row.vgt_id
this.creation.createdAt = params.row.createdAt
this.creation.group = params.row.group
this.creation.id = params.row.id
this.creation.name = params.row.name
this.creation.originalIndex = params.row.originalIndex
this.creation.text = params.row.text
this.creation.vgt_id = params.row.vgt_id
// params.row - row object
// params.pageIndex - index of this row on the current page.
// params.selected - if selection is enabled this argument
// indicates selected or not
// params.event - click event
// params.row - row object
// params.pageIndex - index of this row on the current page.
// params.selected - if selection is enabled this argument
// indicates selected or not
// params.event - click event
},
onSearch(params) {
console.log("onSearch", params)
// params.searchTerm - term being searched for
// params.rowCount - number of rows that match search
//console.log('onSearch', params)
// params.searchTerm - term being searched for
// params.rowCount - number of rows that match search
},
mySearchFn(params){
mySearchFn(params) {
//console.log("mySearchFn", params)
}
}
};
</script>
},
},
}
</script>

View File

@ -1,134 +1,145 @@
<template>
<div>
<b-card-header>User</b-card-header>
<!--Tables-->
<b-row class="mt-5">
<b-col xl="8" class="mb-5 mb-xl-0">
<vue-good-table
:columns="columns"
:rows="rows"
max-height="470px"
:fixed-header="true"
:line-numbers="false"
styleClass="vgt-table striped condensed"
theme="nocturnal"
@on-row-click="onRowClick"
@on-search="onSearch"
:search-options="{
enabled: true,
skipDiacritics: true,
searchFn: mySearchFn,
placeholder: 'durchsuche die tabelle'
}"
:pagination-options="{
enabled: true,
mode: 'pages'
}" >
<div slot="table-actions">
Mitglieder suchen .
</div>
</vue-good-table>
</b-col>
<b-col xl="4" class="mb-5 mb-xl-0">
<!-- card neues mitglied -->
<b-card v-if="showNewUser" body-class="p-0" header-class="border-0">
<template v-slot:header>
<b-row align-v="center">
<b-col>
<h3 class="mb-0">Neues Mitglied anlegen</h3>
</b-col>
<b-col class="text-right">
<a href="#!" class="btn btn-sm btn-primary">speichern</a>
<!--Tables-->
<b-row class="mt-5">
<b-col xl="8" class="mb-5 mb-xl-0">
<vue-good-table
:columns="columns"
:rows="rows"
max-height="470px"
:fixed-header="true"
:line-numbers="false"
styleClass="vgt-table striped condensed"
theme="nocturnal"
@on-row-click="onRowClick"
@on-search="onSearch"
:search-options="{
enabled: true,
skipDiacritics: true,
searchFn: mySearchFn,
placeholder: 'durchsuche die tabelle',
}"
:pagination-options="{
enabled: true,
mode: 'pages',
}"
>
<div slot="table-actions">Mitglieder suchen .</div>
</vue-good-table>
</b-col>
<b-col xl="4" class="mb-5 mb-xl-0">
<!-- card neues mitglied -->
<b-card v-if="showNewUser" body-class="p-0" header-class="border-0">
<template v-slot:header>
<b-row align-v="center">
<b-col>
<h3 class="mb-0">Neues Mitglied anlegen</h3>
</b-col>
<b-col class="text-right">
<a href="#!" class="btn btn-sm btn-primary">speichern</a>
</b-col>
</b-row>
</template>
<b-card-body>
<form>
<b-row class="">
<label class="col-md-3 col-form-label form-control-label">Name</label>
<b-col md="10">
<base-input placeholder="Jon Snow"></base-input>
</b-col>
</b-row>
</template>
<b-card-body>
<form>
<b-row class="">
<label class="col-md-3 col-form-label form-control-label">Name</label>
<b-col md="10">
<base-input placeholder="Jon Snow"></base-input>
</b-col>
</b-row>
<b-row class="">
<label for="example-email-input" class="col-md-3 col-form-label form-control-label">Email</label>
<b-col md="10">
<base-input type="email" placeholder="gradido@example.com" id="example-email-input0" />
</b-col>
</b-row>
<b-row class="">
<label class="col-md-3 col-form-label form-control-label">Gruppe</label>
<b-col md="10">
<base-input placeholder="Gruppe"></base-input>
</b-col>
</b-row>
</form>
</b-card-body>
</b-card>
<!-- card mitglied bearbeiten -->
<b-card v-else body-class="p-0" header-class="border-0">
<template v-slot:header>
<b-row align-v="center">
<b-col>
<h3 class="mb-0">Mitglied Daten</h3>
</b-col>
<b-col class="text-right">
<a href="#!" @click="showNewUser=true" class="btn btn-sm btn-warning">schliesen</a>
<b-row class="">
<label for="example-email-input" class="col-md-3 col-form-label form-control-label">
Email
</label>
<b-col md="10">
<base-input
type="email"
placeholder="gradido@example.com"
id="example-email-input0"
/>
</b-col>
</b-row>
</template>
<b-card-body>
<img style="width: 147px" src="https://demos.creative-tim.com/argon-dashboard-pro/assets/img/theme/team-4.jpg" />
<b-row class="">
<label class="col-md-3 col-form-label form-control-label">Gruppe</label>
<b-col md="10">
<base-input placeholder="Gruppe"></base-input>
</b-col>
</b-row>
</form>
</b-card-body>
</b-card>
<form>
<b-row class="">
<label class="col-md-3 col-form-label form-control-label">Name</label>
<b-col md="10">
<base-input placeholder="name"></base-input>
</b-col>
</b-row>
<b-row class="">
<label for="example-email-input" class="col-md-3 col-form-label form-control-label">Email</label>
<b-col md="10">
<base-input type="email" autocomplete="username email" placeholder="agradido@example.com" id="example-email-input1"/>
</b-col>
</b-row>
<b-row class="">
<label class="col-md-3 col-form-label form-control-label">Gruppe</label>
<b-col md="10">
<base-input placeholder="Gruppe"></base-input>
</b-col>
</b-row>
</form>
<!-- card mitglied bearbeiten -->
<b-card v-else body-class="p-0" header-class="border-0">
<template v-slot:header>
<b-row align-v="center">
<b-col>
<h3 class="mb-0">Mitglied Daten</h3>
</b-col>
<b-col class="text-right">
<a href="#!" @click="showNewUser = true" class="btn btn-sm btn-warning">
schliesen
</a>
</b-col>
</b-row>
</template>
<b-card-body>
<img
style="width: 147px"
src="https://demos.creative-tim.com/argon-dashboard-pro/assets/img/theme/team-4.jpg"
/>
</b-card-body>
</b-card>
</b-col>
</b-row>
<form>
<b-row class="">
<label class="col-md-3 col-form-label form-control-label">Name</label>
<b-col md="10">
<base-input placeholder="name"></base-input>
</b-col>
</b-row>
<b-row class="">
<label for="example-email-input" class="col-md-3 col-form-label form-control-label">
Email
</label>
<b-col md="10">
<base-input
type="email"
autocomplete="username email"
placeholder="agradido@example.com"
id="example-email-input1"
/>
</b-col>
</b-row>
<b-row class="">
<label class="col-md-3 col-form-label form-control-label">Gruppe</label>
<b-col md="10">
<base-input placeholder="Gruppe"></base-input>
</b-col>
</b-row>
</form>
</b-card-body>
</b-card>
</b-col>
</b-row>
</div>
</template>
<script>
export default {
name: 'admin-user-search',
data(){
data() {
return {
showNewUser: true,
formSetUser: {
name:"",
email:"",
group:""
name: '',
email: '',
group: '',
},
formGetUser: {
name:"",
email:"",
group:""
name: '',
email: '',
group: '',
},
columns: [
{
@ -154,50 +165,97 @@ export default {
},
],
rows: [
{ id:1, name:"John", email: "temail@tutanota.com", createdAt: '1976-10-25',group: "gruppe1" },
{ id:2, name:"Jane", email: "temail@tutanota.com", createdAt: '2011-10-31', group: "gruppe2" },
{ id:3, name:"Susan", email: "temail@tutanota.com", createdAt: '2011-10-30', group: "gruppe4" },
{ id:4, name:"Chris", email: "temail@tutanota.com", createdAt: '2011-10-11', group: "gruppe3" },
{ id:5, name:"Dan", email: "temail@tutanota.com", createdAt: '2011-10-21', group: "gruppe1" },
{ id:6, name:"Bohn", email: "temail@tutanota.com", createdAt: '2011-10-31', group: "gruppe2" },
{ id:7, name:"Tellohn", email: "temail@tutanota.com", createdAt: '2009-10-31', group: "gruppe2" },
{ id:7, name:"Tellohn", email: "temail@tutanota.com", createdAt: '2009-10-31', group: "gruppe5" },
{
id: 1,
name: 'John',
email: 'temail@tutanota.com',
createdAt: '1976-10-25',
group: 'gruppe1',
},
{
id: 2,
name: 'Jane',
email: 'temail@tutanota.com',
createdAt: '2011-10-31',
group: 'gruppe2',
},
{
id: 3,
name: 'Susan',
email: 'temail@tutanota.com',
createdAt: '2011-10-30',
group: 'gruppe4',
},
{
id: 4,
name: 'Chris',
email: 'temail@tutanota.com',
createdAt: '2011-10-11',
group: 'gruppe3',
},
{
id: 5,
name: 'Dan',
email: 'temail@tutanota.com',
createdAt: '2011-10-21',
group: 'gruppe1',
},
{
id: 6,
name: 'Bohn',
email: 'temail@tutanota.com',
createdAt: '2011-10-31',
group: 'gruppe2',
},
{
id: 7,
name: 'Tellohn',
email: 'temail@tutanota.com',
createdAt: '2009-10-31',
group: 'gruppe2',
},
{
id: 7,
name: 'Tellohn',
email: 'temail@tutanota.com',
createdAt: '2009-10-31',
group: 'gruppe5',
},
],
};
}
},
methods: {
onRowClick(params) {
console.log(params.row)
this.showNewUser = false
// params.row - row object
// params.pageIndex - index of this row on the current page.
// params.selected - if selection is enabled this argument
// indicates selected or not
// params.event - click event
onRowClick(params) {
//console.log(params.row)
this.showNewUser = false
// params.row - row object
// params.pageIndex - index of this row on the current page.
// params.selected - if selection is enabled this argument
// indicates selected or not
// params.event - click event
},
onSearch(params) {
console.log(params)
// params.searchTerm - term being searched for
// params.rowCount - number of rows that match search
//console.log(params)
// params.searchTerm - term being searched for
// params.rowCount - number of rows that match search
},
mySearchFn(params){
mySearchFn(params) {
//console.log(params)
},
validateEmail(value){
if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(value))
{
this.msg['email'] = '';
} else{
this.msg['email'] = 'Invalid Email Address';
}
validateEmail(value) {
if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(value)) {
this.msg['email'] = ''
} else {
this.msg['email'] = 'Invalid Email Address'
}
},
},
watch: {
mail(value){
mail(value) {
// binding this to the data value in the email input
this.email = value;
this.validateEmail(value);
this.email = value
this.validateEmail(value)
},
}
};
</script>
},
}
</script>

View File

@ -1,19 +1,14 @@
<template>
<div>
default
</div>
<div>default</div>
</template>
<script>
export default {
name: 'default',
data(){
return {
};
data() {
return {}
},
methods: {
},
watch: {
}
};
</script>
methods: {},
watch: {},
}
</script>

View File

@ -1,16 +1,16 @@
<template>
<div>
<base-header class="pb-6 pb-8 pt-5 pt-md-8">
<!-- Card stats -->
<b-row>
<b-col xl="3" md="6">
<stats-card title="Total traffic"
type="gradient-red"
sub-title="350,897"
icon="ni ni-active-40"
class="mb-4">
<stats-card
title="Total traffic"
type="gradient-red"
sub-title="350,897"
icon="ni ni-active-40"
class="mb-4"
>
<template slot="footer">
<span class="text-success mr-2">3.48%</span>
<span class="text-nowrap">Since last month</span>
@ -18,12 +18,13 @@
</stats-card>
</b-col>
<b-col xl="3" md="6">
<stats-card title="Total traffic"
type="gradient-orange"
sub-title="2,356"
icon="ni ni-chart-pie-35"
class="mb-4">
<stats-card
title="Total traffic"
type="gradient-orange"
sub-title="2,356"
icon="ni ni-chart-pie-35"
class="mb-4"
>
<template slot="footer">
<span class="text-success mr-2">12.18%</span>
<span class="text-nowrap">Since last month</span>
@ -31,26 +32,27 @@
</stats-card>
</b-col>
<b-col xl="3" md="6">
<stats-card title="Sales"
type="gradient-green"
sub-title="924"
icon="ni ni-money-coins"
class="mb-4">
<stats-card
title="Sales"
type="gradient-green"
sub-title="924"
icon="ni ni-money-coins"
class="mb-4"
>
<template slot="footer">
<span class="text-danger mr-2">5.72%</span>
<span class="text-nowrap">Since last month</span>
</template>
</stats-card>
</b-col>
<b-col xl="3" md="6">
<stats-card title="Performance"
type="gradient-info"
sub-title="49,65%"
icon="ni ni-chart-bar-32"
class="mb-4">
<stats-card
title="Performance"
type="gradient-info"
sub-title="49,65%"
icon="ni ni-chart-bar-32"
class="mb-4"
>
<template slot="footer">
<span class="text-success mr-2">54.8%</span>
<span class="text-nowrap">Since last month</span>
@ -68,17 +70,18 @@
<b-row align-v="center" slot="header">
<b-col>
<h6 class="text-light text-uppercase ls-1 mb-1">Overview</h6>
<h5 class="h3 text-white mb-0">Sales value</h5>
<h5 class="h3 mb-0">Sales value</h5>
</b-col>
<b-col>
<b-nav class="nav-pills justify-content-end">
<b-nav-item
class="mr-2 mr-md-0"
:active="bigLineChart.activeIndex === 0"
link-classes="py-2 px-3"
@click.prevent="initBigChart(0)">
<span class="d-none d-md-block">Month</span>
<span class="d-md-none">M</span>
class="mr-2 mr-md-0"
:active="bigLineChart.activeIndex === 0"
link-classes="py-2 px-3"
@click.prevent="initBigChart(0)"
>
<span class="d-none d-md-block">Month</span>
<span class="d-md-none">M</span>
</b-nav-item>
<b-nav-item
link-classes="py-2 px-3"
@ -96,8 +99,7 @@
ref="bigChart"
:chart-data="bigLineChart.chartData"
:extra-options="bigLineChart.extraOptions"
>
</line-chart>
></line-chart>
</card>
</b-col>
@ -110,12 +112,7 @@
</b-col>
</b-row>
<bar-chart
:height="350"
ref="barChart"
:chart-data="redBarChart.chartData"
>
</bar-chart>
<bar-chart :height="350" ref="barChart" :chart-data="redBarChart.chartData"></bar-chart>
</card>
</b-col>
</b-row>
@ -132,85 +129,86 @@
</b-row>
<!--End tables-->
</b-container>
</div>
</template>
<script>
// Charts
import * as chartConfigs from '@/components/Charts/config';
import LineChart from '@/components/Charts/LineChart';
import BarChart from '@/components/Charts/BarChart';
// Charts
import * as chartConfigs from '@/components/Charts/config'
import LineChart from '@/components/Charts/LineChart'
import BarChart from '@/components/Charts/BarChart'
// Components
import BaseProgress from '@/components/BaseProgress';
import StatsCard from '@/components/Cards/StatsCard';
// Components
import BaseProgress from '@/components/BaseProgress'
import StatsCard from '@/components/Cards/StatsCard'
// Tables
import SocialTrafficTable from './Dashboard/SocialTrafficTable';
import PageVisitsTable from './Dashboard/PageVisitsTable';
// Tables
import SocialTrafficTable from './Dashboard/SocialTrafficTable'
import PageVisitsTable from './Dashboard/PageVisitsTable'
export default {
components: {
LineChart,
BarChart,
//BaseProgress,
StatsCard,
PageVisitsTable,
SocialTrafficTable
},
data() {
return {
bigLineChart: {
allData: [
[0, 20, 10, 30, 15, 40, 20, 60, 60],
[0, 20, 5, 25, 10, 30, 15, 40, 40]
],
activeIndex: 0,
chartData: {
datasets: [
{
label: 'Performance',
data: [0, 20, 10, 30, 15, 40, 20, 60, 60],
}
],
labels: ['May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
},
extraOptions: chartConfigs.blueChartOptions,
},
redBarChart: {
chartData: {
labels: ['Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
datasets: [{
label: 'Sales',
data: [25, 20, 30, 22, 17, 29]
}]
},
extraOptions: chartConfigs.blueChartOptions
}
};
},
methods: {
initBigChart(index) {
let chartData = {
export default {
components: {
LineChart,
BarChart,
//BaseProgress,
StatsCard,
PageVisitsTable,
SocialTrafficTable,
},
data() {
return {
bigLineChart: {
allData: [
[0, 20, 10, 30, 15, 40, 20, 60, 60],
[0, 20, 5, 25, 10, 30, 15, 40, 40],
],
activeIndex: 0,
chartData: {
datasets: [
{
label: 'Performance',
data: this.bigLineChart.allData[index]
}
data: [0, 20, 10, 30, 15, 40, 20, 60, 60],
},
],
labels: ['May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
};
this.bigLineChart.chartData = chartData;
this.bigLineChart.activeIndex = index;
}
},
mounted() {
this.initBigChart(0);
},
extraOptions: chartConfigs.blueChartOptions,
},
redBarChart: {
chartData: {
labels: ['Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
datasets: [
{
label: 'Sales',
data: [25, 20, 30, 22, 17, 29],
},
],
},
extraOptions: chartConfigs.blueChartOptions,
},
}
};
},
methods: {
initBigChart(index) {
let chartData = {
datasets: [
{
label: 'Performance',
data: this.bigLineChart.allData[index],
},
],
labels: ['May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
}
this.bigLineChart.chartData = chartData
this.bigLineChart.activeIndex = index
},
},
mounted() {
this.initBigChart(0)
},
}
</script>
<style>
.el-table .cell{
.el-table .cell {
padding-left: 0px;
padding-right: 0px;
}

View File

@ -1,58 +1,49 @@
<template>
<div>
<!--Tables-->
<b-row class="mt-5">
<b-col xl="8" class="mb-5 mb-xl-0">
<vue-good-table
:columns="columns"
:rows="rows"
max-height="370px"
:fixed-header="true"
:line-numbers="true"
@on-row-click="onRowClick"
@on-search="onSearch"
:search-options="{
enabled: true,
skipDiacritics: true,
searchFn: mySearchFn,
placeholder: 'durchsuche die tabelle',
externalQuery: searchQuery,
}"
:pagination-options="{
enabled: true,
mode: 'pages'
}"
>
<div slot="table-actions">
Mitglieder suchen .
</div>
</vue-good-table>
</b-col>
<b-col xl="4" class="mb-5 mb-xl-0">
<social-traffic-table></social-traffic-table>
</b-col>
</b-row>
<!--End tables-->
<b-row class="mt-5">
<b-col xl="8" class="mb-5 mb-xl-0">
<vue-good-table
:columns="columns"
:rows="rows"
max-height="370px"
:fixed-header="true"
:line-numbers="true"
@on-row-click="onRowClick"
@on-search="onSearch"
:search-options="{
enabled: true,
skipDiacritics: true,
searchFn: mySearchFn,
placeholder: 'durchsuche die tabelle',
externalQuery: searchQuery,
}"
:pagination-options="{
enabled: true,
mode: 'pages',
}"
>
<div slot="table-actions">Mitglieder suchen .</div>
</vue-good-table>
</b-col>
<b-col xl="4" class="mb-5 mb-xl-0">
<social-traffic-table></social-traffic-table>
</b-col>
</b-row>
<!--End tables-->
</div>
</template>
<script>
import SocialTrafficTable from './SocialTrafficTable';
import SocialTrafficTable from './SocialTrafficTable'
export default {
name: 'my-component',
components: {
SocialTrafficTable
},
data(){
SocialTrafficTable,
},
data() {
return {
columns: [
{
@ -78,34 +69,82 @@ export default {
},
],
rows: [
{ id:1, name:"John", age: 20, createdAt: '1976-10-25',score: 0.03343 },
{ id:2, name:"Jane", age: 24, createdAt: '2011-10-31', score: 0.03343 },
{ id:3, name:"Susan", age: 16, createdAt: '2011-10-30', score: 0.03343 },
{ id:4, name:"Chris", age: 55, createdAt: '2011-10-11', score: 0.03343 },
{ id:5, name:"Dan", age: 40, createdAt: '2011-10-21', score: 0.03343 },
{ id:6, name:"Bohn", age: 70, createdAt: '2011-10-31', score: 0.03343 },
{ id:7, name:"Tellohn", age: 56, createdAt: '2009-10-31', score: 0.13343 },
{ id:7, name:"Tellohn", age: 56, createdAt: '2009-10-31', score: 0.03343 },
{
id: 1,
name: 'John',
age: 20,
createdAt: '1976-10-25',
score: 0.03343,
},
{
id: 2,
name: 'Jane',
age: 24,
createdAt: '2011-10-31',
score: 0.03343,
},
{
id: 3,
name: 'Susan',
age: 16,
createdAt: '2011-10-30',
score: 0.03343,
},
{
id: 4,
name: 'Chris',
age: 55,
createdAt: '2011-10-11',
score: 0.03343,
},
{
id: 5,
name: 'Dan',
age: 40,
createdAt: '2011-10-21',
score: 0.03343,
},
{
id: 6,
name: 'Bohn',
age: 70,
createdAt: '2011-10-31',
score: 0.03343,
},
{
id: 7,
name: 'Tellohn',
age: 56,
createdAt: '2009-10-31',
score: 0.13343,
},
{
id: 7,
name: 'Tellohn',
age: 56,
createdAt: '2009-10-31',
score: 0.03343,
},
],
};
}
},
methods: {
onRowClick(params) {
console.log(params)
// params.row - row object
// params.pageIndex - index of this row on the current page.
// params.selected - if selection is enabled this argument
// indicates selected or not
// params.event - click event
onRowClick(params) {
//console.log(params)
// params.row - row object
// params.pageIndex - index of this row on the current page.
// params.selected - if selection is enabled this argument
// indicates selected or not
// params.event - click event
},
onSearch(params) {
console.log(params)
// params.searchTerm - term being searched for
// params.rowCount - number of rows that match search
//console.log(params)
// params.searchTerm - term being searched for
// params.rowCount - number of rows that match search
},
mySearchFn(params){
console.log(params)
}
}
};
</script>
mySearchFn(params) {
//console.log(params)
},
},
}
</script>

View File

@ -1,5 +1,4 @@
<template>
<b-card body-class="p-0" header-class="border-0">
<template v-slot:header>
<b-row align-v="center">
@ -12,83 +11,70 @@
</b-row>
</template>
<el-table class="table-responsive table"
:data="tableData"
header-row-class-name="thead-light">
<el-table-column label="Page name"
min-width="130px"
prop="page">
<template v-slot="{row}">
<div class="font-weight-600">{{row.page}}</div>
<el-table class="table-responsive table" :data="tableData" header-row-class-name="thead-light">
<el-table-column label="Page name" min-width="130px" prop="page">
<template v-slot="{ row }">
<div class="font-weight-600">{{ row.page }}</div>
</template>
</el-table-column>
<el-table-column label="Visitors"
min-width="70px"
prop="visitors">
</el-table-column>
<el-table-column label="Unique users"
min-width="90px"
prop="unique">
</el-table-column>
<el-table-column label="Visitors" min-width="70px" prop="visitors"></el-table-column>
<el-table-column label="Unique users" min-width="90px" prop="unique"></el-table-column>
<el-table-column label="Bounce rate"
min-width="90px"
prop="bounceRate">
<template v-slot="{row}">
{{row.bounceRate}}
<el-table-column label="Bounce rate" min-width="90px" prop="bounceRate">
<template v-slot="{ row }">
{{ row.bounceRate }}
</template>
</el-table-column>
</el-table>
</b-card>
</template>
<script>
import { Table, TableColumn, DropdownMenu, DropdownItem, Dropdown} from 'element-ui'
export default {
name: 'page-visits-table',
components: {
[Table.name]: Table,
[TableColumn.name]: TableColumn,
[Dropdown.name]: Dropdown,
[DropdownItem.name]: DropdownItem,
[DropdownMenu.name]: DropdownMenu,
},
data() {
return {
tableData: [
{
page: '/argon/',
visitors: '4,569',
unique: '340',
bounceRate: '46,53%'
},
{
page: '/argon/index.html',
visitors: '3,985',
unique: '319',
bounceRate: '46,53%'
},
{
page: '/argon/charts.html',
visitors: '3,513',
unique: '294',
bounceRate: '36,49%'
},
{
page: '/argon/tables.html',
visitors: '2,050',
unique: '147',
bounceRate: '50,87%'
},
{
page: '/argon/profile.html',
visitors: '1,795',
unique: '190',
bounceRate: '46,53%'
}
]
}
import { Table, TableColumn, DropdownMenu, DropdownItem, Dropdown } from 'element-ui'
export default {
name: 'page-visits-table',
components: {
[Table.name]: Table,
[TableColumn.name]: TableColumn,
[Dropdown.name]: Dropdown,
[DropdownItem.name]: DropdownItem,
[DropdownMenu.name]: DropdownMenu,
},
data() {
return {
tableData: [
{
page: '/argon/',
visitors: '4,569',
unique: '340',
bounceRate: '46,53%',
},
{
page: '/argon/index.html',
visitors: '3,985',
unique: '319',
bounceRate: '46,53%',
},
{
page: '/argon/charts.html',
visitors: '3,513',
unique: '294',
bounceRate: '36,49%',
},
{
page: '/argon/tables.html',
visitors: '2,050',
unique: '147',
bounceRate: '50,87%',
},
{
page: '/argon/profile.html',
visitors: '1,795',
unique: '190',
bounceRate: '46,53%',
},
],
}
}
},
}
</script>
<style>
</style>
<style></style>

View File

@ -11,24 +11,19 @@
</b-row>
</template>
<el-table
class="table-responsive table"
:data="tableData"
header-row-class-name="thead-light">
<el-table class="table-responsive table" :data="tableData" header-row-class-name="thead-light">
<el-table-column label="Referral" min-width="115px" prop="name">
<template v-slot="{row}">
<div class="font-weight-600">{{row.name}}</div>
<template v-slot="{ row }">
<div class="font-weight-600">{{ row.name }}</div>
</template>
</el-table-column>
<el-table-column label="Visitors" min-width="110px" prop="visitors">
</el-table-column>
<el-table-column label="Visitors" min-width="110px" prop="visitors"></el-table-column>
<el-table-column min-width="220px" prop="progress">
<template v-slot="{row}">
<template v-slot="{ row }">
<div class="d-flex align-items-center">
<span class="mr-2">{{row.progress}}%</span>
<span class="mr-2">{{ row.progress }}%</span>
<base-progress :type="row.progressType" :value="row.progress" />
</div>
</template>
@ -37,56 +32,54 @@
</b-card>
</template>
<script>
import { BaseProgress } from '@/components';
import { Table, TableColumn, DropdownMenu, DropdownItem, Dropdown} from 'element-ui'
export default {
name: 'social-traffic-table',
components: {
BaseProgress,
[Table.name]: Table,
[TableColumn.name]: TableColumn,
[Dropdown.name]: Dropdown,
[DropdownItem.name]: DropdownItem,
[DropdownMenu.name]: DropdownMenu,
},
data() {
return {
tableData: [
{
name: 'Facebook',
visitors: '1,480',
progress: 60,
progressType: 'gradient-danger',
},
{
name: 'LinkedIn',
visitors: '5,480',
progress: 70,
progressType: 'gradient-success',
},
{
name: 'Google',
visitors: '4,807',
progress: 80,
progressType: 'gradient-primary',
},
{
name: 'Instagram',
visitors: '3,678',
progress: 75,
progressType: 'gradient-info',
},
{
name: 'Twitter',
visitors: '2,645',
progress: 30,
progressType: 'gradient-warning',
}
]
}
import { BaseProgress } from '@/components'
import { Table, TableColumn, DropdownMenu, DropdownItem, Dropdown } from 'element-ui'
export default {
name: 'social-traffic-table',
components: {
BaseProgress,
[Table.name]: Table,
[TableColumn.name]: TableColumn,
[Dropdown.name]: Dropdown,
[DropdownItem.name]: DropdownItem,
[DropdownMenu.name]: DropdownMenu,
},
data() {
return {
tableData: [
{
name: 'Facebook',
visitors: '1,480',
progress: 60,
progressType: 'gradient-danger',
},
{
name: 'LinkedIn',
visitors: '5,480',
progress: 70,
progressType: 'gradient-success',
},
{
name: 'Google',
visitors: '4,807',
progress: 80,
progressType: 'gradient-primary',
},
{
name: 'Instagram',
visitors: '3,678',
progress: 75,
progressType: 'gradient-info',
},
{
name: 'Twitter',
visitors: '2,645',
progress: 30,
progressType: 'gradient-warning',
},
],
}
}
},
}
</script>
<style>
</style>
<style></style>

View File

@ -4,12 +4,13 @@
<!-- Card stats -->
<b-row>
<b-col xl="3" md="6">
<stats-card title="Total traffic"
type="gradient-red"
sub-title="350,897"
icon="ni ni-active-40"
class="mb-4">
<stats-card
title="Total traffic"
type="gradient-red"
sub-title="350,897"
icon="ni ni-active-40"
class="mb-4"
>
<template slot="footer">
<span class="text-success mr-2">3.48%</span>
<span class="text-nowrap">Since last month</span>
@ -17,12 +18,13 @@
</stats-card>
</b-col>
<b-col xl="3" md="6">
<stats-card title="Total traffic"
type="gradient-orange"
sub-title="2,356"
icon="ni ni-chart-pie-35"
class="mb-4">
<stats-card
title="Total traffic"
type="gradient-orange"
sub-title="2,356"
icon="ni ni-chart-pie-35"
class="mb-4"
>
<template slot="footer">
<span class="text-success mr-2">12.18%</span>
<span class="text-nowrap">Since last month</span>
@ -30,26 +32,27 @@
</stats-card>
</b-col>
<b-col xl="3" md="6">
<stats-card title="Sales"
type="gradient-green"
sub-title="924"
icon="ni ni-money-coins"
class="mb-4">
<stats-card
title="Sales"
type="gradient-green"
sub-title="924"
icon="ni ni-money-coins"
class="mb-4"
>
<template slot="footer">
<span class="text-danger mr-2">5.72%</span>
<span class="text-nowrap">Since last month</span>
</template>
</stats-card>
</b-col>
<b-col xl="3" md="6">
<stats-card title="Performance"
type="gradient-info"
sub-title="49,65%"
icon="ni ni-chart-bar-32"
class="mb-4">
<stats-card
title="Performance"
type="gradient-info"
sub-title="49,65%"
icon="ni ni-chart-bar-32"
class="mb-4"
>
<template slot="footer">
<span class="text-success mr-2">54.8%</span>
<span class="text-nowrap">Since last month</span>
@ -63,8 +66,7 @@
<b-row>
<b-col>
<b-card no-body class="border-0">
<div id="map-custom" class="map-canvas"
style="height: 600px;"></div>
<div id="map-custom" class="map-canvas" style="height: 600px"></div>
</b-card>
</b-col>
</b-row>
@ -72,79 +74,96 @@
</div>
</template>
<script>
import { API_KEY } from './Maps/API_KEY';
import GoogleMapsLoader from 'google-maps';
import { API_KEY } from './Maps/API_KEY'
import GoogleMapsLoader from 'google-maps'
GoogleMapsLoader.KEY = API_KEY;
GoogleMapsLoader.KEY = API_KEY
export default {
methods: {
initMap(google) {
let map, lat = 40.748817, lng = -73.985428, color = "#5e72e4";
map = document.getElementById('map-custom');
export default {
methods: {
initMap(google) {
let map,
lat = 40.748817,
lng = -73.985428,
color = '#5e72e4'
map = document.getElementById('map-custom')
let myLatlng = new google.maps.LatLng(lat, lng);
let mapOptions = {
zoom: 12,
scrollwheel: false,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP,
styles: [{
"featureType": "administrative",
"elementType": "labels.text.fill",
"stylers": [{ "color": "#444444" }]
}, {
"featureType": "landscape",
"elementType": "all",
"stylers": [{ "color": "#f2f2f2" }]
}, {
"featureType": "poi",
"elementType": "all",
"stylers": [{ "visibility": "off" }]
}, {
"featureType": "road",
"elementType": "all",
"stylers": [{ "saturation": -100 }, { "lightness": 45 }]
}, {
"featureType": "road.highway",
"elementType": "all",
"stylers": [{ "visibility": "simplified" }]
}, {
"featureType": "road.arterial",
"elementType": "labels.icon",
"stylers": [{ "visibility": "off" }]
}, {
"featureType": "transit",
"elementType": "all",
"stylers": [{ "visibility": "off" }]
}, { "featureType": "water", "elementType": "all", "stylers": [{ "color": color }, { "visibility": "on" }] }]
}
map = new google.maps.Map(map, mapOptions);
let marker = new google.maps.Marker({
position: myLatlng,
map: map,
animation: google.maps.Animation.DROP,
title: 'Hello World!'
});
let contentString = '<div class="info-window-content"><h2>Argon Dashboard PRO</h2>' +
'<p>A beautiful premium dashboard for Bootstrap 4.</p></div>';
let infowindow = new google.maps.InfoWindow({
content: contentString
});
google.maps.event.addListener(marker, 'click', function () {
infowindow.open(map, marker);
});
let myLatlng = new google.maps.LatLng(lat, lng)
let mapOptions = {
zoom: 12,
scrollwheel: false,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP,
styles: [
{
featureType: 'administrative',
elementType: 'labels.text.fill',
stylers: [{ color: '#444444' }],
},
{
featureType: 'landscape',
elementType: 'all',
stylers: [{ color: '#f2f2f2' }],
},
{
featureType: 'poi',
elementType: 'all',
stylers: [{ visibility: 'off' }],
},
{
featureType: 'road',
elementType: 'all',
stylers: [{ saturation: -100 }, { lightness: 45 }],
},
{
featureType: 'road.highway',
elementType: 'all',
stylers: [{ visibility: 'simplified' }],
},
{
featureType: 'road.arterial',
elementType: 'labels.icon',
stylers: [{ visibility: 'off' }],
},
{
featureType: 'transit',
elementType: 'all',
stylers: [{ visibility: 'off' }],
},
{
featureType: 'water',
elementType: 'all',
stylers: [{ color: color }, { visibility: 'on' }],
},
],
}
map = new google.maps.Map(map, mapOptions)
let marker = new google.maps.Marker({
position: myLatlng,
map: map,
animation: google.maps.Animation.DROP,
title: 'Hello World!',
})
let contentString =
'<div class="info-window-content"><h2>Argon Dashboard PRO</h2>' +
'<p>A beautiful premium dashboard for Bootstrap 4.</p></div>'
let infowindow = new google.maps.InfoWindow({
content: contentString,
})
google.maps.event.addListener(marker, 'click', function () {
infowindow.open(map, marker)
})
},
mounted() {
GoogleMapsLoader.load(google => {
this.initMap(google);
});
}
};
},
mounted() {
GoogleMapsLoader.load((google) => {
this.initMap(google)
})
},
}
</script>

Some files were not shown because too many files have changed in this diff Show More