50 Commits

Author SHA1 Message Date
kandrusyak
a2dd48be65 add settings.json 2022-12-14 15:54:31 +03:00
kandrusyak
bd8268d848 add build 2022-12-12 21:51:58 +03:00
kandrusyak
4ca691608a add Google Analytics 2022-12-12 15:14:07 +03:00
kandrusyak
5939bb4e13 add unlock transaction 2022-12-09 11:53:36 +03:00
kandrusyak
68e665804c add compute fee 2022-12-09 11:24:21 +03:00
kandrusyak
2efc3acd94 add cursor pointer, add server detail animation 2022-12-06 20:12:10 +03:00
kandrusyak
428153a9d9 fix server display name and conf file name 2022-12-06 18:27:02 +03:00
kandrusyak
78f48b240b some fixes 2022-12-06 16:16:12 +03:00
kandrusyak
8bf83aaa5b some fixes 2022-12-05 23:09:54 +03:00
kandrusyak
e0753a0779 fix statuses and dns 2022-12-05 21:18:16 +03:00
kandrusyak
9c331789b3 fix vpn servers 2022-12-05 19:45:45 +03:00
kandrusyak
3e8a0e2233 fix header mistake 2022-12-01 16:21:02 +03:00
kandrusyak
a8bb75b2f5 add prettier 2022-12-01 13:50:14 +03:00
kandrusyak
8e4b6c832e remove mock 2022-12-01 13:44:35 +03:00
kandrusyak
0927c0dca5 fix blank screen 2022-11-27 02:51:35 +03:00
kandrusyak
a555d18417 add vpn and delegates 2022-11-27 02:45:33 +03:00
kandrusyak
ea4d8a4049 Revert "add messages api"
This reverts commit 2210b4ab91.
2022-10-05 17:22:11 +03:00
kandrusyak
1448d3e7da Revert "add messages api"
This reverts commit e07353cd87.
2022-10-05 17:22:07 +03:00
kandrusyak
e07353cd87 add messages api 2022-10-05 16:42:19 +03:00
kandrusyak
2210b4ab91 add messages api 2022-10-05 16:16:44 +03:00
kandrusyak
8e5c0a3edc validate data 2022-09-13 18:10:07 +03:00
kandrusyak
4f73075cf9 share data rework 2022-09-08 13:20:51 +03:00
Kirill Andrusyak
b68ef748ca Merge pull request #59 from franze6/DID-14
Added the ability to share account data
2022-09-07 18:54:07 +03:00
DwCay
200f4f58a7 Added the ability to share account data 2022-08-30 22:49:13 +03:00
kandrusyak
86a6bf013d fix processed status 2022-08-26 15:25:33 +03:00
Kirill Andrusyak
6afa08e5f5 Merge pull request #58 from franze6/DID-13 2022-08-24 20:24:43 +03:00
DwCay
3173ec9342 Fix style for status markers 2022-08-24 19:41:42 +03:00
DuCay
c8352e6537 Merge branch 'master' into DID-13 2022-08-24 17:21:00 +03:00
DwCay
9fbc6423d1 Refactoring code and fix problems display blockchain data 2022-08-24 17:00:43 +03:00
kandrusyak
e4f691ab78 add processed fields 2022-08-24 13:24:10 +03:00
Kirill Andrusyak
446276fadf Merge pull request #57 from franze6/DID-13
Fixed delete and update data account
2022-08-24 10:08:39 +03:00
DwCay
1b574ac347 Fixed delete and update data account 2022-08-24 06:04:21 +03:00
Kirill Andrusyak
0c7db9cd30 Merge pull request #56 from franze6/DID-13
Did 13
2022-08-23 11:36:53 +03:00
DwCay
fc977cc43a Fix decrypted data for blockchain data 2022-08-23 11:17:34 +03:00
DwCay
0a0151d1dd Added location statuses for account data 2022-08-23 05:50:59 +03:00
kandrusyak
369f0e375c add loader 2022-08-22 17:05:05 +03:00
kandrusyak
79ca03823c Change timeout 2022-08-22 15:53:00 +03:00
kandrusyak
49f3ee17a0 tr fix 2022-08-22 15:46:39 +03:00
kandrusyak
841e82e233 update dockerfile 2022-08-17 16:24:49 +03:00
Kirill Andrusyak
2212ea7433 Create docker-image.yml 2022-08-17 15:46:40 +03:00
kandrusyak
f257066d5a blockchain integration 2022-08-17 14:05:49 +03:00
Kirill Andrusyak
027bf0d405 Merge pull request #55 from franze6/DID-12 2022-08-16 17:17:41 +03:00
DwCay
663cfacf63 Removed avatar display in updated and added sender avatar generation in validations 2022-08-16 15:54:55 +03:00
kandrusyak
27ee899402 clean up and bug fixes 2022-08-09 17:52:03 +03:00
kandrusyak
59dd4c9554 fix some bugs 2022-08-09 00:14:21 +03:00
kandrusyak
c659e17344 improve performance 2022-08-08 19:00:56 +03:00
DuCay
49d86ed74b Merge pull request #53 from franze6/DID-12
Changed methods for fetch data transactions and for get passPhrase
2022-08-03 12:04:15 +03:00
DwCay
b2d5df9533 changed methods for encrypt 2022-08-03 12:02:44 +03:00
Daria Golova
0eea3d1ea3 changed userData to accountData 2022-08-03 11:22:07 +03:00
DwCay
a86dd13e0e Changed methods for fetch data transactions and for get passPhrase 2022-08-03 00:48:32 +03:00
169 changed files with 21094 additions and 24694 deletions

64
.github/workflows/docker-image.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Build and deploy
on:
push:
branches:
- 'master'
tags:
- '*'
paths-ignore:
- '.github/**'
pull_request:
branches:
- 'master'
tags:
- '*'
paths-ignore:
- '.github/**'
workflow_dispatch:
inputs:
logLevel:
description: 'Log level'
required: true
default: 'warning'
type: choice
options:
- info
- warning
- debug
jobs:
build:
runs-on: self-hosted
steps:
- name: Get branch data
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v2
with:
push: true
tags: kandrusyak/blockchain-ui:master
deploy:
needs: build
name: Deploy to host
runs-on: self-hosted
steps:
- name: Get branch data
uses: actions/checkout@v2
- name: Deploying changes
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
password: ${{ secrets.SSH_PASSWORD }}
script: |
docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }}
./update.sh

19
.prettierrc Normal file
View File

@@ -0,0 +1,19 @@
{
"arrowParens": "always",
"useTabs": false,
"tabWidth": 2,
"singleQuote": true,
"endOfLine": "lf",
"jsxBracketSameLine": false,
"trailingComma": "es5",
"printWidth": 100,
"overrides": [
{
"files": "*.json",
"options": {
"singleQuote": false,
"parser": "json"
}
}
]
}

View File

@@ -1,11 +1,13 @@
FROM node:16-alpine AS builder FROM node:16-alpine AS builder
RUN apk --update add --no-cache curl git python3 alpine-sdk bash autoconf libtool automake
WORKDIR /front WORKDIR /front
COPY . . COPY . .
RUN npm install RUN npm install
RUN npm run build RUN npm run build:prod
FROM nginx:1.21.6-alpine as runner FROM nginx:1.21.6-alpine as runner

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

View File

@@ -0,0 +1,4 @@
<svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg">
<circle fill="#0EA5E9" cx="18" cy="18" r="18" />
<path d="M18 26a8 8 0 1 1 8-8 8.009 8.009 0 0 1-8 8Zm0-14a6 6 0 1 0 0 12 6 6 0 0 0 0-12Z" fill="#F0F9FF" />
</svg>

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,4 @@
<svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg">
<circle fill="#6366F1" cx="18" cy="18" r="18" />
<path d="M24.446 19.335a2.5 2.5 0 00-3.522 3.194c-.845.63-1.87.97-2.924.971a4.979 4.979 0 01-1.113-.135 4.436 4.436 0 01-1.343 1.682 6.91 6.91 0 006.9-1.165 2.5 2.5 0 002-4.547h.002zM20.431 11.938a2.5 2.5 0 10-.4 2.014 5.027 5.027 0 012.723 3.078c.148-.018.297-.028.446-.03a4.5 4.5 0 011.7.334 7.023 7.023 0 00-4.469-5.396zM14.969 20.25a2.49 2.49 0 00-1.932-1.234A4.624 4.624 0 0113 18.5a4.97 4.97 0 011.348-3.391 4.456 4.456 0 01-.788-2.016A6.989 6.989 0 0011 18.5c.003.391.04.781.11 1.166a2.5 2.5 0 103.859.584z" fill="#EEF2FF" />
</svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@@ -0,0 +1,10 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="18" cy="18" r="18" fill="#E1E1F7"/>
<rect x="6" y="6" width="24" height="24" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_21_1525" transform="scale(0.0125)"/>
</pattern>
<image id="image0_21_1525" width="80" height="80" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAUKADAAQAAAABAAAAUAAAAAAx4ExPAAAHHUlEQVR4Ae1bWYwVRRRlwBGXaBRxRQaMGvDDiAG/lCigjHEDE/VHPox8uGCi4o8mij/G6Iea6JcJ0TCSEDVKYhC3EOPglhgMGoMxKswQBRWXuG8snjNTd7xTr+u9qq7q9/pp3eRQ1bfvPffW7br9+r0eJkzIkiuQK5ArkCuQK5ArkCuQK5ArkCuQK5ArkCvw/6pAT5uWOw1xLgQWAWcCUw3+xrgL2ApsAJ4B/gJccixOLAb6gdnACQB1oTxw6Q45G2k+DewFDnhgGDaXA7acAcU6YB8Qw2Pz1vZ4MjJ7TC32d8yfB24B5gLckbQ5AmBxbgC2ACzOfuA2gNILPAroC8CdeitwDjAdeAt4FyDPjYDmoV3XyYnIWBbBwj0AsNVaCW8nKwEWkFgGvAGwqH8C24BBwJbNUGg9eW4HyMEd2w90jXBHvQ9w0Z8Ac4BQuQcOuk3/wPF5oSSwF54fMX8N4MU5Eqi1vIDsuHjuliklM50EvyFAF/ErHJ8LiLBtufNs0XofHtu/o8eXIDoXvQfoS5DJ4eBYAGwCyPsLMBug2G07qi3Wc9dtBHYDNo/4dXzkfecjk+CKxNlwJw0Y7rcjuMmzNgFPRApu13kmsSGMB7nNSp/hbmQbcwfpViahblseixTpuRv5wVbEI35B48Qga7fxEnNqPUY+cqSWX0G42pAutcj5acuC2FKk/wlG7xlDm8f2b+uxPG7wW0JVsgjELFRMGzO3VDzkSiafgomLOy0ZYyPRSSbGFxiL2pMePnrNQ58oSdXC8qDM+1RV8oMh5uNRUXvytI/+e8Vjpp0f5MZ8aIWpHAZu7nLeD2PkYDin4BnJIdUObMdVlQdz2YlSRJ+2FVuOfCak2Dyj2sB/UxVQkjkmMH6IuXDLxRJfn7YVW47ylGDzaJu2zzcjItviggojy6fn65Exzoc/c43lGUkj1Q78zixKdok5TDoIN3dOaNtq+2kmqyQ7MFUBd5qkTk5asvFk080hY4W2rbaXHCXn8VECj1J97Ro2cWcExg8xF+4hOPH3viKZX6SETuvlQgw5bIPUqXbgDhN1ZlD0MOM+Yy4XS7x1e4qOo0svxbR5tK/3PFUBvzQRZ3pHDjeUHbjLctXtqU+59EcZI5tH+7Z9/iwiHgA+rzDydhODb+5iJBVPTA4Nvj9DwwLyfUhVQm7GYCxXe/roNU90rqlaWD7RTo/OyE0g3Izlak8fveZxR2vzmUcQj7tjVYVxyc0YD0fGSMUTmcZ4d3kfwgUKxluUPxI+GS+2qHzali7iL6PNY9H6HaZqYX5Bvx+Q78SMzl89YkVzkPs+4GWL1Kdte5WPi0eZdHb6McLzKp+SII1TDRdfk8bIDDgzp1iehhxS7UBNLEnO0sqSc+EQTqHxbVuxf9xMbB45X3qsooDvmGyuKZ3Vv47CIZxyxqdtxZZjnzmwebRNbeb8eZ9/osY3YMdFZHU8fPnMRy7Oywpz4Ev5WJ6y8Uv5DcCL95znSnmPOq03HGsMR2jbiv06i8fQ1XvgHz3uMYnfXSLVe43vNxinGn/+aDto5npopudFJDSP9q31vB/Z8S+ruICHAjLlgzJ96HtRgJ9t+iAUKXhs3rYeX2YWwYX4Cm2JSwFpQ9vXR695bP9kx1V8CuvkNuiDwPmLsA/9tC2yJ09Xi+wE30WE2rt4U/G4+Ef0Ve/ApsEDT/q0bSBlvHmVBeTD66qIFOlLDpH9mHBX2eLSi53NI/pajj3Iir/MvALsA6SNQt4/0Fb8yPESUObT2OZhTsyNOdZO+HbvWuADQBbPv2FZAywAQnY6bekzAJBD+LZivgzoBXzamTwLAfLILiUXc2Suqd5Igqq8TIbrCmAHIAv9DPOVgLy8wbS0kOMOgO9ahH8Y873Aq4AtzR6qt8B4JyA82zFn7lxDR+RKRNUL4w65CuCVTy3kvBpgDF0AxgsR/r54HbANEB6ugWtpm8xCpE2AJPAh5lcAPUDVwhiMxZgSfzfm3Hm2NGtznlsKaB6uiWurVHgPkjdw32J+EzCp0ojF5Ix5M8AcWEje45YDWljUQa0wc623ebg23h+TC+8TqwG56k9hfnTyKOGEU+CyFpC8nsD8kHCakf8UpHm4Vq45iTAhPkowSX4qXg/UTZYjIfnEfhNz/S6FuTZrZ+5IEc2zEcoyF0O4xkZeVRbva+CsMW39JsyNOTJX7iAtLFKrdhb7Zjxi4z32w5IJ/QbUuXiyoDkmV+a8UJQlRs2zuIT/mAtfITKZO8c09Z/cZXLmT/i6PSVz33YWHt6+nDLReWb0xFxz/skWdnU6Lbn2IilefFv4ie2jF555NkHIMQMVBQvh6IRtqrxb8tTi+2AbKsy25c6bb8VqpbfMGw9bfXvoxt3XuMp4jbNOre6B8aEzQ65ArkCuQK5ArkCuQK5ArkCuQK5ArkCuQK5ArsB/qgL/AIxZGoBhTSTCAAAAAElFTkSuQmCC"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
dist/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

1
dist/index.html vendored Normal file
View File

@@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="UTF-8"><title>React App</title><link rel="icon" href="favicon.ico"><script defer="defer" src="main.3ab1a28d.js"></script></head><body><div id="root"></div><script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-M6E6PDB6Y4")</script><script async src="https://www.googletagmanager.com/gtag/js?id=G-M6E6PDB6Y4"></script></body></html>

8
dist/main.3ab1a28d.js vendored Normal file

File diff suppressed because one or more lines are too long

96
dist/main.3ab1a28d.js.LICENSE.txt vendored Normal file
View File

@@ -0,0 +1,96 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/*! https://mths.be/cssesc v3.0.0 by @mathias */
/*! https://mths.be/punycode v1.3.2 by @mathias */
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/**
* @license qrcode.react
* Copyright (c) Paul O'Shannessy
* SPDX-License-Identifier: ISC
*/
/**
* React Router DOM v6.3.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/**
* React Router v6.3.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license URI.js v4.4.1 (c) 2011 Gary Court. License: http://github.com/garycourt/uri-js */
//! moment.js
//! moment.js locale configuration

3
dist/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"API_URL": "https://tn-alpha.idntty.org:8080/api/"
}

1091
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,33 +2,36 @@
"name": "mosaic-react", "name": "mosaic-react",
"version": "0.1.0", "version": "0.1.0",
"scripts": { "scripts": {
"dev": "webpack-dev-server", "dev": "webpack-dev-server --https",
"build": "webpack" "build:dev": "webpack --mode development --progress",
"build:prod": "webpack --mode production --progress"
}, },
"dependencies": { "dependencies": {
"@liskhq/lisk-client": "^5.2.2", "@liskhq/lisk-client": "^5.2.2",
"@tailwindcss/forms": "^0.4.0", "@tailwindcss/forms": "^0.4.0",
"autoprefixer": "^10.4.5",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"chart.js": "^3.5.0", "chart.js": "^3.5.0",
"chartjs-adapter-moment": "^1.0.0", "chartjs-adapter-moment": "^1.0.0",
"classnames": "^2.3.2",
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0",
"mobx": "^6.6.0", "mobx": "^6.6.0",
"mobx-react-lite": "^3.4.0", "mobx-react-lite": "^3.4.0",
"moment": "^2.29.1", "moment": "^2.29.1",
"net": "^1.0.2", "net": "^1.0.2",
"os-browserify": "^0.3.0", "os-browserify": "^0.3.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"postcss-loader": "^7.0.0", "prettier": "^2.8.0",
"process": "^0.11.10", "process": "^0.11.10",
"qrcode.react": "^3.1.0",
"querystring-es3": "^0.2.1", "querystring-es3": "^0.2.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-country-flag": "^3.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-flatpickr": "^3.10.7", "react-flatpickr": "^3.10.7",
"react-loader-spinner": "^5.2.0",
"react-router-dom": "^6.2.1", "react-router-dom": "^6.2.1",
"react-transition-group": "^4.4.1", "react-transition-group": "^4.4.1",
"sodium-universal": "^3.1.0",
"stream-browserify": "^3.0.0", "stream-browserify": "^3.0.0",
"url": "^0.11.0", "url": "^0.11.0",
"util": "^0.12.4" "util": "^0.12.4"
@@ -37,10 +40,14 @@
"@babel/core": "^7.18.5", "@babel/core": "^7.18.5",
"@babel/preset-env": "^7.18.2", "@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.17.12", "@babel/preset-react": "^7.17.12",
"autoprefixer": "^10.4.5",
"babel-loader": "^8.2.5", "babel-loader": "^8.2.5",
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"file-loader": "^6.2.0",
"html-loader": "^3.1.2", "html-loader": "^3.1.2",
"html-webpack-plugin": "^5.5.0",
"postcss": "^8.4.5", "postcss": "^8.4.5",
"postcss-loader": "^7.0.0",
"sass": "^1.52.3", "sass": "^1.52.3",
"sass-loader": "^13.0.0", "sass-loader": "^13.0.0",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",

View File

@@ -7,5 +7,13 @@
<body> <body>
<div id="root"></div> <div id="root"></div>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-M6E6PDB6Y4');
</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-M6E6PDB6Y4"></script>
</body> </body>
</html> </html>

3
public/settings.json Normal file
View File

@@ -0,0 +1,3 @@
{
"API_URL": "https://tn-alpha.idntty.org:8080/api/"
}

View File

@@ -1,71 +1,63 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { import { Routes, Route, useLocation } from 'react-router-dom';
Routes,
Route,
useLocation,
} from 'react-router-dom';
import './css/style.scss'; import './css/style.scss';
import './charts/ChartjsConfig';
// Import pages // Import pages
import Dashboard from './pages/Dashboard';
import Analytics from './pages/Analytics';
import Fintech from './pages/Fintech';
import ProfileId from './pages/digitalId/ProfileId'; import ProfileId from './pages/digitalId/ProfileId';
import Validate from './pages/digitalId/Validate';
import ValidationLog from './pages/digitalId/ValidationLog'; import ValidationLog from './pages/digitalId/ValidationLog';
import Verify from './pages/digitalId/Verify'; import Verify from './pages/digitalId/Verify';
import Messages from './pages/Messages'; import Onboarding1 from './pages/Onboarding1';
import ButtonPage from './pages/component/ButtonPage'; import Onboarding2 from './pages/Onboarding2';
import BadgePage from './pages/component/BadgePage'; import Onboarding3 from './pages/Onboarding3';
import AccordionPage from './pages/component/AccordionPage'; import Onboarding4 from './pages/Onboarding4';
import Onboarding1 from "./pages/Onboarding1"; import SharedData from './pages/SharedData';
import Onboarding2 from "./pages/Onboarding2"; import SignIn from './pages/SignIn';
import Onboarding3 from "./pages/Onboarding3"; import { LoadingOverlay } from './components/LoadingOverlay';
import Onboarding4 from "./pages/Onboarding4"; import { store } from './store/store';
import SharedData from "./pages/SharedData"; import { observer } from 'mobx-react-lite';
import ResetPassword from "./pages/ResetPassword"; import Delegates from './pages/services/Delegates';
import SignIn from "./pages/SignIn"; import VPNServers from './pages/services/VPNServers';
import VPNPrepare from './pages/services/VPNPrepare';
function App() { import { NotificationContainer } from './components/NotificationContainer';
const App = observer(() => {
const location = useLocation(); const location = useLocation();
useEffect(()=> { useEffect(() => {
localStorage.setItem('svgAvatar', '') localStorage.setItem('svgAvatar', '');
},[]) }, []);
useEffect(() => { useEffect(() => {
document.querySelector('html').style.scrollBehavior = 'auto' document.querySelector('html').style.scrollBehavior = 'auto';
window.scroll({ top: 0 }) window.scroll({ top: 0 });
document.querySelector('html').style.scrollBehavior = '' document.querySelector('html').style.scrollBehavior = '';
}, [location.pathname]); // triggered on route change }, [location.pathname]); // triggered on route change
if (!store.nodeInfo.networkIdentifier) {
return <LoadingOverlay />;
}
return ( return (
<> <>
{store.loading && <LoadingOverlay />}
<NotificationContainer />
<Routes> <Routes>
<Route exact path="/" element={<Onboarding1 />} /> <Route exact path="/" element={<Onboarding1 />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/dashboard/analytics" element={<Analytics />} />
<Route path="/dashboard/fintech" element={<Fintech />} />
<Route path="/digitalId/profile-id" element={<ProfileId />} /> <Route path="/digitalId/profile-id" element={<ProfileId />} />
<Route path="/digitalId/validate" element={<Validate />} /> {/*<Route path="/digitalId/validate" element={<Validate />} />*/}
<Route path="/digitalId/validation-log" element={<ValidationLog />} /> <Route path="/digitalId/validation-log" element={<ValidationLog />} />
<Route path="/digitalId/verify" element={<Verify />} /> <Route path="/digitalId/verify" element={<Verify />} />
<Route path="/messages" element={<Messages />} />
<Route path="/onboarding-2" element={<Onboarding2 />} /> <Route path="/onboarding-2" element={<Onboarding2 />} />
<Route path="/onboarding-3" element={<Onboarding3 />} /> <Route path="/onboarding-3" element={<Onboarding3 />} />
<Route path="/onboarding-4" element={<Onboarding4 />} /> <Route path="/onboarding-4" element={<Onboarding4 />} />
<Route path="/component/button" element={<ButtonPage />} /> <Route path="/services/delegates" element={<Delegates />} />
<Route path="/shared-data" element={<SharedData />} /> <Route path="/services/vpn" element={<VPNPrepare />} />
<Route path="/component/badge" element={<BadgePage />} /> <Route path="/services/vpn/servers" element={<VPNServers />} />
<Route path="/component/accordion" element={<AccordionPage />} /> <Route path="/shared-data/:id" element={<SharedData />} />
<Route path="/reset-password" element={<ResetPassword />} />
<Route path="/signIn" element={<SignIn />} /> <Route path="/signIn" element={<SignIn />} />
</Routes> </Routes>
</> </>
); );
} });
export default App; export default App;

View File

@@ -1,3 +1,3 @@
import {fetchWrapper} from '../shared/fetchWrapper'; import { fetchWrapper } from '../shared/fetchWrapper';
export const getNodeInfo = (headers) => fetchWrapper.get('node/info/', headers) export const getNodeInfo = (headers) => fetchWrapper.get('node/info/', headers);

View File

@@ -1,162 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
function BarChart01({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 5,
callback: (value) => formatValue(value),
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.marginRight = tailwindConfig().theme.margin[4];
// Button element
const button = document.createElement('button');
button.style.display = 'inline-flex';
button.style.alignItems = 'center';
button.style.opacity = item.hidden ? '.3' : '';
button.onclick = () => {
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex));
c.update();
};
// Color box
const box = document.createElement('span');
box.style.display = 'block';
box.style.width = tailwindConfig().theme.width[3];
box.style.height = tailwindConfig().theme.height[3];
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
box.style.marginRight = tailwindConfig().theme.margin[2];
box.style.borderWidth = '3px';
box.style.borderColor = item.fillStyle;
box.style.pointerEvents = 'none';
// Label
const labelContainer = document.createElement('span');
labelContainer.style.display = 'flex';
labelContainer.style.alignItems = 'center';
const value = document.createElement('span');
value.style.color = tailwindConfig().theme.colors.slate[800];
value.style.fontSize = tailwindConfig().theme.fontSize['3xl'][0];
value.style.lineHeight = tailwindConfig().theme.fontSize['3xl'][1].lineHeight;
value.style.fontWeight = tailwindConfig().theme.fontWeight.bold;
value.style.marginRight = tailwindConfig().theme.margin[2];
value.style.pointerEvents = 'none';
const label = document.createElement('span');
label.style.color = tailwindConfig().theme.colors.slate[500];
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
const theValue = c.data.datasets[item.datasetIndex].data.reduce((a, b) => a + b, 0);
const valueText = document.createTextNode(formatValue(theValue));
const labelText = document.createTextNode(item.text);
value.appendChild(valueText);
label.appendChild(labelText);
li.appendChild(button);
button.appendChild(box);
button.appendChild(labelContainer);
labelContainer.appendChild(value);
labelContainer.appendChild(label);
ul.appendChild(li);
});
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-3">
<ul ref={legend} className="flex flex-wrap"></ul>
</div>
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default BarChart01;

View File

@@ -1,99 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatValue } from '../utils/Utils';
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
function BarChart02({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
stacked: true,
grid: {
drawBorder: false,
},
beginAtZero: true,
ticks: {
maxTicksLimit: 5,
callback: (value) => formatValue(value),
},
},
x: {
stacked: true,
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 200,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default BarChart02;

View File

@@ -1,156 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatThousands } from '../utils/Utils';
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
function BarChart03({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
stacked: true,
grid: {
drawBorder: false,
},
beginAtZero: true,
ticks: {
maxTicksLimit: 5,
callback: (value) => formatThousands(value),
},
},
x: {
stacked: true,
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatThousands(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current
if (!ul) return
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove()
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c)
items.forEach((item) => {
const li = document.createElement('li')
li.style.marginRight = tailwindConfig().theme.margin[3];
// Button element
const button = document.createElement('button')
button.style.display = 'inline-flex';
button.style.alignItems = 'center';
button.style.opacity = item.hidden ? '.3' : '';
button.onclick = () => {
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex))
c.update()
};
// Color box
const box = document.createElement('span')
box.style.display = 'block';
box.style.width = tailwindConfig().theme.width[3];
box.style.height = tailwindConfig().theme.height[3];
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
box.style.marginRight = tailwindConfig().theme.margin[2];
box.style.borderWidth = '3px';
box.style.borderColor = item.fillStyle;
box.style.pointerEvents = 'none';
// Label
const label = document.createElement('span')
label.style.color = tailwindConfig().theme.colors.slate[500];
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
const labelText = document.createTextNode(item.text)
label.appendChild(labelText)
li.appendChild(button)
button.appendChild(box)
button.appendChild(label)
ul.appendChild(li)
})
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-4">
<div className="grow mb-1">
<ul ref={legend} className="flex flex-wrap"></ul>
</div>
</div>
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default BarChart03;

View File

@@ -1,149 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatThousands } from '../utils/Utils';
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
function BarChart04({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
indexAxis: 'y',
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM',
},
},
grid: {
display: false,
drawBorder: false,
},
},
x: {
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 3,
align: 'end',
callback: (value) => formatThousands(value),
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatThousands(context.parsed.x),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current
if (!ul) return
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove()
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c)
items.forEach((item) => {
const li = document.createElement('li')
li.style.marginRight = tailwindConfig().theme.margin[4]
// Button element
const button = document.createElement('button')
button.style.display = 'inline-flex'
button.style.alignItems = 'center'
button.style.opacity = item.hidden ? '.3' : ''
button.onclick = () => {
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex))
c.update()
}
// Color box
const box = document.createElement('span')
box.style.display = 'block'
box.style.width = tailwindConfig().theme.width[3]
box.style.height = tailwindConfig().theme.height[3]
box.style.borderRadius = tailwindConfig().theme.borderRadius.full
box.style.marginRight = tailwindConfig().theme.margin[2]
box.style.borderWidth = '3px'
box.style.borderColor = item.fillStyle
box.style.pointerEvents = 'none'
// Label
const label = document.createElement('span')
label.style.color = tailwindConfig().theme.colors.slate[500]
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0]
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight
const labelText = document.createTextNode(item.text)
label.appendChild(labelText)
li.appendChild(button)
button.appendChild(box)
button.appendChild(label)
ul.appendChild(li)
})
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-4">
<ul ref={legend} className="flex flex-wrap"></ul>
</div>
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default BarChart04;

View File

@@ -1,157 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend);
function BarChart05({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 5,
callback: (value) => formatValue(value),
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [
{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.marginRight = tailwindConfig().theme.margin[4];
// Button element
const button = document.createElement('button');
button.style.display = 'inline-flex';
button.style.alignItems = 'center';
button.style.opacity = item.hidden ? '.3' : '';
button.onclick = () => {
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex));
c.update();
};
// Color box
const box = document.createElement('span');
box.style.display = 'block';
box.style.width = tailwindConfig().theme.width[3];
box.style.height = tailwindConfig().theme.height[3];
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
box.style.marginRight = tailwindConfig().theme.margin[2];
box.style.borderWidth = '3px';
box.style.borderColor = item.fillStyle;
box.style.pointerEvents = 'none';
// Label
const label = document.createElement('span');
label.style.color = tailwindConfig().theme.colors.slate[500];
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
const labelText = document.createTextNode(item.text);
label.appendChild(labelText);
li.appendChild(button);
button.appendChild(box);
button.appendChild(label);
ul.appendChild(li);
});
},
},
],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-3">
<div className="flex flex-wrap justify-between items-center">
<div className="flex items-center">
<div className="text-3xl font-bold text-slate-800 mr-2">$1,347.09</div>
<div className="text-sm">Net</div>
</div>
<div className="grow ml-2">
<ul ref={legend} className="flex flex-wrap justify-end"></ul>
</div>
</div>
</div>
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default BarChart05;

View File

@@ -1,164 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, BarController, BarElement, LinearScale, CategoryScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
// Import images
import revolutIcon from '../images/company-icon-01.svg';
import hsbcIcon from '../images/company-icon-02.svg';
import qontoIcon from '../images/company-icon-03.svg';
import n26Icon from '../images/company-icon-04.svg';
Chart.register(BarController, BarElement, LinearScale, CategoryScale, Tooltip, Legend);
const images = [revolutIcon, hsbcIcon, qontoIcon, n26Icon];
function BarChart06({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
indexAxis: 'y',
layout: {
padding: {
top: 12,
bottom: 16,
left: 72,
right: 20,
},
},
scales: {
y: {
grid: {
display: false,
drawBorder: false,
drawTicks: false,
},
ticks: {
display: false,
},
},
x: {
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 3,
align: 'end',
callback: (value) => formatValue(value),
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.x),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [
{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.marginRight = tailwindConfig().theme.margin[4];
// Button element
const button = document.createElement('button');
button.style.display = 'inline-flex';
button.style.alignItems = 'center';
button.style.opacity = item.hidden ? '.3' : '';
button.onclick = () => {
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex));
c.update();
};
// Color box
const box = document.createElement('span');
box.style.display = 'block';
box.style.width = tailwindConfig().theme.width[3];
box.style.height = tailwindConfig().theme.height[3];
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
box.style.marginRight = tailwindConfig().theme.margin[2];
box.style.borderWidth = '3px';
box.style.borderColor = item.fillStyle;
box.style.pointerEvents = 'none';
// Label
const label = document.createElement('span');
label.style.color = tailwindConfig().theme.colors.slate[500];
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
const labelText = document.createTextNode(item.text);
label.appendChild(labelText);
li.appendChild(button);
button.appendChild(box);
button.appendChild(label);
ul.appendChild(li);
});
},
afterDraw(c) {
const xAxis = c.scales.x;
const yAxis = c.scales.y;
yAxis.ticks.forEach((value, index) => {
const y = yAxis.getPixelForTick(index);
const image = new Image();
image.src = images[index];
c.ctx.drawImage(image, xAxis.left - 52, y - 18);
});
},
},
],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-4">
<ul ref={legend} className="flex flex-wrap"></ul>
</div>
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default BarChart06;

View File

@@ -1,42 +0,0 @@
// Import Chart.js
import { Chart, Tooltip } from 'chart.js';
// Import Tailwind config
import { tailwindConfig } from '../utils/Utils';
Chart.register(Tooltip);
// Define Chart.js default settings
Chart.defaults.font.family = '"Inter", sans-serif';
Chart.defaults.font.weight = '500';
Chart.defaults.color = tailwindConfig().theme.colors.slate[400];
Chart.defaults.scale.grid.color = tailwindConfig().theme.colors.slate[100];
Chart.defaults.plugins.tooltip.titleColor = tailwindConfig().theme.colors.slate[800];
Chart.defaults.plugins.tooltip.bodyColor = tailwindConfig().theme.colors.slate[800];
Chart.defaults.plugins.tooltip.backgroundColor = tailwindConfig().theme.colors.white;
Chart.defaults.plugins.tooltip.borderWidth = 1;
Chart.defaults.plugins.tooltip.borderColor = tailwindConfig().theme.colors.slate[200];
Chart.defaults.plugins.tooltip.displayColors = false;
Chart.defaults.plugins.tooltip.mode = 'nearest';
Chart.defaults.plugins.tooltip.intersect = false;
Chart.defaults.plugins.tooltip.position = 'nearest';
Chart.defaults.plugins.tooltip.caretSize = 0;
Chart.defaults.plugins.tooltip.caretPadding = 20;
Chart.defaults.plugins.tooltip.cornerRadius = 4;
Chart.defaults.plugins.tooltip.padding = 8;
// Register Chart.js plugin to add a bg option for chart area
Chart.register({
id: 'chartAreaPlugin',
// eslint-disable-next-line object-shorthand
beforeDraw: (chart) => {
if (chart.config.options.chartArea && chart.config.options.chartArea.backgroundColor) {
const ctx = chart.canvas.getContext('2d');
const { chartArea } = chart;
ctx.save();
ctx.fillStyle = chart.config.options.chartArea.backgroundColor;
// eslint-disable-next-line max-len
ctx.fillRect(chartArea.left, chartArea.top, chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
ctx.restore();
}
},
});

View File

@@ -1,114 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, DoughnutController, ArcElement, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig } from '../utils/Utils';
Chart.register(DoughnutController, ArcElement, TimeScale, Tooltip);
function DoughnutChart({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'doughnut',
data: data,
options: {
cutout: '80%',
layout: {
padding: 24,
},
plugins: {
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.margin = tailwindConfig().theme.margin[1];
// Button element
const button = document.createElement('button');
button.classList.add('btn-xs');
button.style.backgroundColor = tailwindConfig().theme.colors.white;
button.style.borderWidth = tailwindConfig().theme.borderWidth[1];
button.style.borderColor = tailwindConfig().theme.colors.slate[200];
button.style.color = tailwindConfig().theme.colors.slate[500];
button.style.boxShadow = tailwindConfig().theme.boxShadow.md;
button.style.opacity = item.hidden ? '.3' : '';
button.onclick = () => {
c.toggleDataVisibility(item.index, !item.index);
c.update();
};
// Color box
const box = document.createElement('span');
box.style.display = 'block';
box.style.width = tailwindConfig().theme.width[2];
box.style.height = tailwindConfig().theme.height[2];
box.style.backgroundColor = item.fillStyle;
box.style.borderRadius = tailwindConfig().theme.borderRadius.sm;
box.style.marginRight = tailwindConfig().theme.margin[1];
box.style.pointerEvents = 'none';
// Label
const label = document.createElement('span');
label.style.display = 'flex';
label.style.alignItems = 'center';
const labelText = document.createTextNode(item.text);
label.appendChild(labelText);
li.appendChild(button);
button.appendChild(box);
button.appendChild(label);
ul.appendChild(li);
});
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="grow flex flex-col justify-center">
<div>
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
<div className="px-5 pt-2 pb-6">
<ul ref={legend} className="flex flex-wrap justify-center -m-1"></ul>
</div>
</div>
);
}
export default DoughnutChart;

View File

@@ -1,76 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart01({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
chartArea: {
backgroundColor: tailwindConfig().theme.colors.slate[50],
},
layout: {
padding: 20,
},
scales: {
y: {
display: false,
beginAtZero: true,
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
},
display: false,
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
resizeDelay: 200,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart01;

View File

@@ -1,153 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart02({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: 20,
},
scales: {
y: {
grid: {
drawBorder: false,
beginAtZero: true,
},
ticks: {
maxTicksLimit: 5,
callback: (value) => formatValue(value),
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.slice(0, 2).forEach((item) => {
const li = document.createElement('li');
li.style.marginLeft = tailwindConfig().theme.margin[3];
// Button element
const button = document.createElement('button');
button.style.display = 'inline-flex';
button.style.alignItems = 'center';
button.style.opacity = item.hidden ? '.3' : '';
button.onclick = () => {
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex));
c.update();
};
// Color box
const box = document.createElement('span');
box.style.display = 'block';
box.style.width = tailwindConfig().theme.width[3];
box.style.height = tailwindConfig().theme.height[3];
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
box.style.marginRight = tailwindConfig().theme.margin[2];
box.style.borderWidth = '3px';
box.style.borderColor = c.data.datasets[item.datasetIndex].borderColor;
box.style.pointerEvents = 'none';
// Label
const label = document.createElement('span');
label.style.color = tailwindConfig().theme.colors.slate[500];
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
const labelText = document.createTextNode(item.text);
label.appendChild(labelText);
li.appendChild(button);
button.appendChild(box);
button.appendChild(label);
ul.appendChild(li);
});
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-3">
<div className="flex flex-wrap justify-between items-end">
<div className="flex items-start">
<div className="text-3xl font-bold text-slate-800 mr-2">$1,482</div>
<div className="text-sm font-semibold text-white px-1.5 bg-amber-500 rounded-full">-22%</div>
</div>
<div className="grow ml-2 mb-1">
<ul ref={legend} className="flex flex-wrap justify-end"></ul>
</div>
</div>
</div>
{/* Chart built with Chart.js 3 */}
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default LineChart02;

View File

@@ -1,88 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatThousands } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart03({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: 20,
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false,
},
ticks: {
callback: (value) => formatThousands(value),
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatThousands(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
resizeDelay: 200,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart03;

View File

@@ -1,78 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatThousands } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart04({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
chartArea: {
backgroundColor: tailwindConfig().theme.colors.slate[50],
},
layout: {
padding: {
left: 20,
right: 20,
},
},
scales: {
y: {
display: false,
beginAtZero: true,
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
},
display: false,
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatThousands(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart04;

View File

@@ -1,157 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart05({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: 20,
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 7,
callback: (value) => `${value}%`,
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => `${context.parsed.y}%`,
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [
{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.marginLeft = tailwindConfig().theme.margin[3];
// Button element
const button = document.createElement('button');
button.style.display = 'inline-flex';
button.style.alignItems = 'center';
button.style.opacity = item.hidden ? '.3' : '';
button.onclick = () => {
c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex));
c.update();
};
// Color box
const box = document.createElement('span');
box.style.display = 'block';
box.style.width = tailwindConfig().theme.width[3];
box.style.height = tailwindConfig().theme.height[3];
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
box.style.marginRight = tailwindConfig().theme.margin[2];
box.style.borderWidth = '3px';
box.style.borderColor = c.data.datasets[item.datasetIndex].borderColor;
box.style.pointerEvents = 'none';
// Label
const label = document.createElement('span');
label.style.color = tailwindConfig().theme.colors.slate[500];
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
const labelText = document.createTextNode(item.text);
label.appendChild(labelText);
li.appendChild(button);
button.appendChild(box);
button.appendChild(label);
ul.appendChild(li);
});
},
},
],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<div className="px-5 py-3">
<div className="flex flex-wrap justify-between items-end">
<div className="flex items-center">
<div className="text-3xl font-bold text-slate-800 mr-2">244.7%</div>
<div className="text-sm">
<span className="font-medium text-slate-800">17.4%</span> AVG
</div>
</div>
<div className="grow ml-2 mb-1">
<ul ref={legend} className="flex flex-wrap justify-end" />
</div>
</div>
</div>
{/* Chart built with Chart.js 3 */}
<div className="grow">
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default LineChart05;

View File

@@ -1,94 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart06({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 7,
callback: (value) => formatValue(value),
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
displayFormats: {
month: 'MMM YY',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
resizeDelay: 200,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart06;

View File

@@ -1,86 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, CategoryScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, CategoryScale, Tooltip);
function LineChart07({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: {
top: 12,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false,
},
ticks: {
maxTicksLimit: 7,
callback: (value) => formatValue(value),
},
},
x: {
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
align: 'end',
},
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart07;

View File

@@ -1,84 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart08({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: {
top: 16,
bottom: 16,
left: 20,
right: 20,
},
},
scales: {
y: {
beginAtZero: true,
grid: {
drawBorder: false,
drawTicks: false,
},
ticks: {
maxTicksLimit: 2,
display: false,
},
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
},
display: false,
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart08;

View File

@@ -1,69 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function LineChart09({
data,
width,
height
}) {
const canvas = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
scales: {
y: {
display: false,
beginAtZero: true,
},
x: {
type: 'time',
time: {
parser: 'MM-DD-YYYY',
unit: 'month',
},
display: false,
},
},
plugins: {
tooltip: {
callbacks: {
title: () => false, // Disable tooltip title
label: (context) => formatValue(context.parsed.y),
},
},
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
maintainAspectRatio: false,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<canvas ref={canvas} width={width} height={height}></canvas>
);
}
export default LineChart09;

View File

@@ -1,117 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, PieController, ArcElement, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig } from '../utils/Utils';
Chart.register(PieController, ArcElement, TimeScale, Tooltip);
function PieChart({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'pie',
data: data,
options: {
layout: {
padding: {
top: 4,
bottom: 4,
left: 24,
right: 24,
},
},
plugins: {
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 200,
},
maintainAspectRatio: false,
},
plugins: [
{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current;
if (!ul) return;
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c);
items.forEach((item) => {
const li = document.createElement('li');
li.style.margin = tailwindConfig().theme.margin[1.5];
// Button element
const button = document.createElement('button');
button.style.display = 'inline-flex';
button.style.alignItems = 'center';
button.style.opacity = item.hidden ? '.3' : '';
button.onclick = () => {
c.toggleDataVisibility(item.index, !item.index);
c.update();
};
// Color box
const box = document.createElement('span');
box.style.display = 'block';
box.style.width = tailwindConfig().theme.width[3];
box.style.height = tailwindConfig().theme.height[3];
box.style.borderRadius = tailwindConfig().theme.borderRadius.full;
box.style.marginRight = tailwindConfig().theme.margin[1.5];
box.style.borderWidth = '3px';
box.style.borderColor = item.fillStyle;
box.style.pointerEvents = 'none';
// Label
const label = document.createElement('span');
label.style.color = tailwindConfig().theme.colors.slate[500];
label.style.fontSize = tailwindConfig().theme.fontSize.sm[0];
label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight;
const labelText = document.createTextNode(item.text);
label.appendChild(labelText);
li.appendChild(button);
button.appendChild(box);
button.appendChild(label);
ul.appendChild(li);
});
},
},
],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="grow flex flex-col justify-center">
<div>
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
<div className="px-5 py-4">
<ul ref={legend} className="flex flex-wrap justify-center -m-1" />
</div>
</div>
);
}
export default PieChart;

View File

@@ -1,113 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, PolarAreaController, RadialLinearScale, Tooltip, Legend,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig } from '../utils/Utils';
Chart.register(PolarAreaController, RadialLinearScale, Tooltip, Legend);
function PolarChart({
data,
width,
height
}) {
const canvas = useRef(null);
const legend = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'polarArea',
data: data,
options: {
layout: {
padding: 24,
},
plugins: {
legend: {
display: false,
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: {
duration: 500,
},
maintainAspectRatio: false,
resizeDelay: 200,
},
plugins: [{
id: 'htmlLegend',
afterUpdate(c, args, options) {
const ul = legend.current
if (!ul) return
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove()
}
// Reuse the built-in legendItems generator
const items = c.options.plugins.legend.labels.generateLabels(c)
items.forEach((item) => {
const li = document.createElement('li')
li.style.margin = tailwindConfig().theme.margin[1]
// Button element
const button = document.createElement('button')
button.classList.add('btn-xs')
button.style.backgroundColor = tailwindConfig().theme.colors.white
button.style.borderWidth = tailwindConfig().theme.borderWidth[1]
button.style.borderColor = tailwindConfig().theme.colors.slate[200]
button.style.color = tailwindConfig().theme.colors.slate[500]
button.style.boxShadow = tailwindConfig().theme.boxShadow.md
button.style.opacity = item.hidden ? '.3' : ''
button.onclick = () => {
c.toggleDataVisibility(item.index, !item.index)
c.update()
}
// Color box
const box = document.createElement('span')
box.style.display = 'block'
box.style.width = tailwindConfig().theme.width[2]
box.style.height = tailwindConfig().theme.height[2]
box.style.backgroundColor = item.fillStyle
box.style.borderRadius = tailwindConfig().theme.borderRadius.sm
box.style.marginRight = tailwindConfig().theme.margin[1]
box.style.pointerEvents = 'none'
// Label
const label = document.createElement('span')
label.style.display = 'flex'
label.style.alignItems = 'center'
const labelText = document.createTextNode(item.text)
label.appendChild(labelText)
li.appendChild(button)
button.appendChild(box)
button.appendChild(label)
ul.appendChild(li)
})
},
}],
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="grow flex flex-col justify-center">
<div>
<canvas ref={canvas} width={width} height={height}></canvas>
</div>
<div className="px-5 pt-2 pb-6">
<ul ref={legend} className="flex flex-wrap justify-center -m-1"></ul>
</div>
</div>
);
}
export default PolarChart;

View File

@@ -1,120 +0,0 @@
import React, { useRef, useEffect } from 'react';
import {
Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip,
} from 'chart.js';
import 'chartjs-adapter-moment';
// Import utilities
import { tailwindConfig, formatValue } from '../utils/Utils';
Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip);
function RealtimeChart({
data,
width,
height
}) {
const canvas = useRef(null);
const chartValue = useRef(null);
const chartDeviation = useRef(null);
useEffect(() => {
const ctx = canvas.current;
// eslint-disable-next-line no-unused-vars
const chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
layout: {
padding: 20,
},
scales: {
y: {
grid: {
drawBorder: false,
},
suggestedMin: 30,
suggestedMax: 80,
ticks: {
maxTicksLimit: 5,
callback: (value) => formatValue(value),
},
},
x: {
type: 'time',
time: {
parser: 'hh:mm:ss',
unit: 'second',
tooltipFormat: 'MMM DD, H:mm:ss a',
displayFormats: {
second: 'H:mm:ss',
},
},
grid: {
display: false,
drawBorder: false,
},
ticks: {
autoSkipPadding: 48,
maxRotation: 0,
},
},
},
plugins: {
legend: {
display: false,
},
tooltip: {
titleFont: {
weight: '600',
},
callbacks: {
label: (context) => formatValue(context.parsed.y),
},
},
},
interaction: {
intersect: false,
mode: 'nearest',
},
animation: false,
maintainAspectRatio: false,
resizeDelay: 200,
},
});
return () => chart.destroy();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data]);
// Update header values
useEffect(() => {
const currentValue = data.datasets[0].data[data.datasets[0].data.length - 1];
const previousValue = data.datasets[0].data[data.datasets[0].data.length - 2];
const diff = ((currentValue - previousValue) / previousValue) * 100;
chartValue.current.innerHTML = data.datasets[0].data[data.datasets[0].data.length - 1];
if (diff < 0) {
chartDeviation.current.style.backgroundColor = tailwindConfig().theme.colors.yellow[500];
} else {
chartDeviation.current.style.backgroundColor = tailwindConfig().theme.colors.emerald[500];
}
chartDeviation.current.innerHTML = `${diff > 0 ? '+' : ''}${diff.toFixed(2)}%`;
}, [data]);
return (
<React.Fragment>
<div className="px-5 py-3">
<div className="flex items-start">
<div className="text-3xl font-bold text-slate-800 mr-2 tabular-nums">$<span ref={chartValue}>57.81</span></div>
<div ref={chartDeviation} className="text-sm font-semibold text-white px-1.5 rounded-full"></div>
</div>
</div>
<div className="grow">
<canvas ref={canvas} data={data} width={width} height={height}></canvas>
</div>
</React.Fragment>
);
}
export default RealtimeChart;

View File

@@ -1,26 +0,0 @@
import React, { useState } from 'react';
function AccordionBasic(props) {
const [open, setOpen] = useState(false);
return (
<div className="px-5 py-4 rounded-sm border border-slate-200">
<button
className="flex items-center justify-between w-full group mb-1"
aria-expanded={open}
onClick={() => setOpen(!open)}
>
<div className="text-sm text-slate-800 font-medium">{props.title}</div>
<svg className={`w-8 h-8 shrink-0 fill-current text-slate-400 group-hover:text-slate-500 ml-3 ${open && 'rotate-180'}`} viewBox="0 0 32 32">
<path d="M16 20l-5.4-5.4 1.4-1.4 4 4 4-4 1.4 1.4z" />
</svg>
</button>
<div className={`text-sm ${!open && 'hidden'}`}>
{props.children}
</div>
</div>
);
}
export default AccordionBasic;

View File

@@ -1,73 +0,0 @@
import React, { useState } from 'react';
function AccordionTableItem(props) {
const [open, setOpen] = useState(false);
return (
<tbody className="text-sm">
<tr>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="flex items-center text-slate-800">
<div className="w-10 h-10 shrink-0 flex items-center justify-center bg-slate-100 rounded-full mr-2 sm:mr-3">
<img className="rounded-full ml-1" src={props.image} width="40" height="40" alt={props.customer} />
</div>
<div className="font-medium text-slate-800">{props.customer}</div>
</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="text-left font-medium text-emerald-500">{props.total}</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="inline-flex font-medium bg-amber-100 text-amber-600 rounded-full text-center px-2.5 py-0.5">{props.status}</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="text-center">{props.items}</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="text-left">{props.location}</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="flex items-center">
<svg className="w-4 h-4 fill-current text-slate-400 shrink-0 mr-2" viewBox="0 0 16 16">
<path d="M4.3 4.5c1.9-1.9 5.1-1.9 7 0 .7.7 1.2 1.7 1.4 2.7l2-.3c-.2-1.5-.9-2.8-1.9-3.8C10.1.4 5.7.4 2.9 3.1L.7.9 0 7.3l6.4-.7-2.1-2.1zM15.6 8.7l-6.4.7 2.1 2.1c-1.9 1.9-5.1 1.9-7 0-.7-.7-1.2-1.7-1.4-2.7l-2 .3c.2 1.5.9 2.8 1.9 3.8 1.4 1.4 3.1 2 4.9 2 1.8 0 3.6-.7 4.9-2l2.2 2.2.8-6.4z" />
</svg>
<div>{props.type}</div>
</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap w-px">
<div className="flex items-center">
<button
className={`text-slate-400 hover:text-slate-500 transform ${open && 'rotate-180'}`}
aria-expanded={open}
onClick={() => setOpen(!open)}
aria-controls={`description-${props.id}`}
>
<span className="sr-only">Menu</span>
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32">
<path d="M16 20l-5.4-5.4 1.4-1.4 4 4 4-4 1.4 1.4z" />
</svg>
</button>
</div>
</td>
</tr>
{/*
Example of content revealing when clicking the button on the right side:
Note that you must set a "colSpan" attribute on the <td> element,
and it should match the number of columns in your table
*/}
<tr id={`description-${props.id}`} role="region" className={`${!open && 'hidden'}`}>
<td colSpan="10" className="px-2 first:pl-5 last:pr-5 py-3">
<div className="flex items-center bg-slate-50 p-3 -mt-3">
<svg className="w-4 h-4 shrink-0 fill-current text-slate-400 mr-2">
<path d="M1 16h3c.3 0 .5-.1.7-.3l11-11c.4-.4.4-1 0-1.4l-3-3c-.4-.4-1-.4-1.4 0l-11 11c-.2.2-.3.4-.3.7v3c0 .6.4 1 1 1zm1-3.6l10-10L13.6 4l-10 10H2v-1.6z" />
</svg>
<div className="italic">{props.description}</div>
</div>
</td>
</tr>
</tbody>
);
}
export default AccordionTableItem;

View File

@@ -1,66 +0,0 @@
import React, { useState } from 'react';
function AccordionTableRichItem(props) {
const [open, setOpen] = useState(false);
return (
<tbody className="text-sm">
<tr>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="flex items-center text-slate-800">
<div className="w-10 h-10 shrink-0 flex items-center justify-center bg-slate-100 rounded-full mr-2 sm:mr-3">
<img className="rounded-full ml-1" src={props.image} width="40" height="40" alt={props.customer} />
</div>
<div className="font-medium text-slate-800">{props.customer}</div>
</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="text-left">{props.email}</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="text-left">{props.location}</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="text-left">{props.date}</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="text-left text-emerald-500 font-medium">{props.amount}</div>
</td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap w-px">
<div className="flex items-center">
<button
className={`text-slate-400 hover:text-slate-500 transform ${open && 'rotate-180'}`}
aria-expanded={open}
onClick={() => setOpen(!open)}
aria-controls={`description-${props.id}`}
>
<span className="sr-only">Menu</span>
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32">
<path d="M16 20l-5.4-5.4 1.4-1.4 4 4 4-4 1.4 1.4z" />
</svg>
</button>
</div>
</td>
</tr>
{/*
Example of content revealing when clicking the button on the right side:
Note that you must set a "colSpan" attribute on the <td> element,
and it should match the number of columns in your table
*/}
<tr id={`description-${props.id}`} role="region" className={`${!open && 'hidden'}`}>
<td colSpan="10" className="px-2 first:pl-5 last:pr-5 py-3">
<div className="bg-slate-50 p-3 -mt-3">
<div className="text-sm mb-3">
<div className="font-medium text-slate-800 mb-1">{props.descriptionTitle}</div>
<div>{props.descriptionBody}</div>
</div>
<button className="btn-xs bg-indigo-500 hover:bg-indigo-600 text-white">Approve</button>
</div>
</td>
</tr>
</tbody>
);
}
export default AccordionTableRichItem;

View File

@@ -1,38 +0,0 @@
import React from 'react';
import Flatpickr from 'react-flatpickr';
function Datepicker({
align
}) {
const options = {
mode: 'range',
static: true,
monthSelectorType: 'static',
dateFormat: 'M j, Y',
defaultDate: [new Date().setDate(new Date().getDate() - 6), new Date()],
prevArrow: '<svg class="fill-current" width="7" height="11" viewBox="0 0 7 11"><path d="M5.4 10.8l1.4-1.4-4-4 4-4L5.4 0 0 5.4z" /></svg>',
nextArrow: '<svg class="fill-current" width="7" height="11" viewBox="0 0 7 11"><path d="M1.4 10.8L0 9.4l4-4-4-4L1.4 0l5.4 5.4z" /></svg>',
onReady: (selectedDates, dateStr, instance) => {
instance.element.value = dateStr.replace('to', '-');
const customClass = (align) ? align : '';
instance.calendarContainer.classList.add(`flatpickr-${customClass}`);
},
onChange: (selectedDates, dateStr, instance) => {
instance.element.value = dateStr.replace('to', '-');
},
}
return (
<div className="relative">
<Flatpickr className="form-input pl-9 text-slate-500 hover:text-slate-600 font-medium focus:border-slate-300 w-60" options={options} />
<div className="absolute inset-0 right-auto flex items-center pointer-events-none">
<svg className="w-4 h-4 fill-current text-slate-500 ml-3" viewBox="0 0 16 16">
<path d="M15 2h-2V0h-2v2H9V0H7v2H5V0H3v2H1a1 1 0 00-1 1v12a1 1 0 001 1h14a1 1 0 001-1V3a1 1 0 00-1-1zm-1 12H2V6h12v8z" />
</svg>
</div>
</div>
);
}
export default Datepicker;

View File

@@ -1,75 +0,0 @@
import React, { useState, useRef, useEffect } from 'react';
import Transition from '../utils/Transition';
function DropdownEditMenu({
children,
align,
...rest
}) {
const [dropdownOpen, setDropdownOpen] = useState(false);
const trigger = useRef(null);
const dropdown = useRef(null);
// close on click outside
useEffect(() => {
const clickHandler = ({ target }) => {
if (!dropdown.current) return;
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
setDropdownOpen(false);
};
document.addEventListener('click', clickHandler);
return () => document.removeEventListener('click', clickHandler);
});
// close if the esc key is pressed
useEffect(() => {
const keyHandler = ({ keyCode }) => {
if (!dropdownOpen || keyCode !== 27) return;
setDropdownOpen(false);
};
document.addEventListener('keydown', keyHandler);
return () => document.removeEventListener('keydown', keyHandler);
});
return (
<div {...rest}>
<button
ref={trigger}
className={`bg-white text-slate-400 hover:text-slate-500 rounded-full ${dropdownOpen && 'bg-slate-100 text-slate-500'}`}
aria-haspopup="true"
onClick={() => setDropdownOpen(!dropdownOpen)}
aria-expanded={dropdownOpen}
>
<span className="sr-only">Menu</span>
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32">
<circle cx="16" cy="16" r="2" />
<circle cx="10" cy="16" r="2" />
<circle cx="22" cy="16" r="2" />
</svg>
</button>
<Transition
show={dropdownOpen}
tag="div"
className={`origin-top-right z-10 absolute top-full min-w-36 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`}
enter="transition ease-out duration-200 transform"
enterStart="opacity-0 -translate-y-2"
enterEnd="opacity-100 translate-y-0"
leave="transition ease-out duration-200"
leaveStart="opacity-100"
leaveEnd="opacity-0"
>
<ul
ref={dropdown}
onFocus={() => setDropdownOpen(true)}
onBlur={() => setDropdownOpen(false)}
>
{children}
</ul>
</Transition>
</div>
);
}
export default DropdownEditMenu;

View File

@@ -1,115 +0,0 @@
import React, { useState, useRef, useEffect } from 'react';
import Transition from '../utils/Transition';
function DropdownFilter({
align
}) {
const [dropdownOpen, setDropdownOpen] = useState(false);
const trigger = useRef(null);
const dropdown = useRef(null);
// close on click outside
useEffect(() => {
const clickHandler = ({ target }) => {
if (!dropdown.current) return;
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
setDropdownOpen(false);
};
document.addEventListener('click', clickHandler);
return () => document.removeEventListener('click', clickHandler);
});
// close if the esc key is pressed
useEffect(() => {
const keyHandler = ({ keyCode }) => {
if (!dropdownOpen || keyCode !== 27) return;
setDropdownOpen(false);
};
document.addEventListener('keydown', keyHandler);
return () => document.removeEventListener('keydown', keyHandler);
});
return (
<div className="relative inline-flex">
<button
ref={trigger}
className="btn bg-white border-slate-200 hover:border-slate-300 text-slate-500 hover:text-slate-600"
aria-haspopup="true"
onClick={() => setDropdownOpen(!dropdownOpen)}
aria-expanded={dropdownOpen}
>
<span className="sr-only">Filter</span><wbr />
<svg className="w-4 h-4 fill-current" viewBox="0 0 16 16">
<path d="M9 15H7a1 1 0 010-2h2a1 1 0 010 2zM11 11H5a1 1 0 010-2h6a1 1 0 010 2zM13 7H3a1 1 0 010-2h10a1 1 0 010 2zM15 3H1a1 1 0 010-2h14a1 1 0 010 2z" />
</svg>
</button>
<Transition
show={dropdownOpen}
tag="div"
className={`origin-top-right z-10 absolute top-full min-w-56 bg-white border border-slate-200 pt-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`}
enter="transition ease-out duration-200 transform"
enterStart="opacity-0 -translate-y-2"
enterEnd="opacity-100 translate-y-0"
leave="transition ease-out duration-200"
leaveStart="opacity-100"
leaveEnd="opacity-0"
>
<div ref={dropdown}>
<div className="text-xs font-semibold text-slate-400 uppercase pt-1.5 pb-2 px-4">Filters</div>
<ul className="mb-4">
<li className="py-1 px-3">
<label className="flex items-center">
<input type="checkbox" className="form-checkbox" />
<span className="text-sm font-medium ml-2">Direct VS Indirect</span>
</label>
</li>
<li className="py-1 px-3">
<label className="flex items-center">
<input type="checkbox" className="form-checkbox" />
<span className="text-sm font-medium ml-2">Real Time Value</span>
</label>
</li>
<li className="py-1 px-3">
<label className="flex items-center">
<input type="checkbox" className="form-checkbox" />
<span className="text-sm font-medium ml-2">Top Channels</span>
</label>
</li>
<li className="py-1 px-3">
<label className="flex items-center">
<input type="checkbox" className="form-checkbox" />
<span className="text-sm font-medium ml-2">Sales VS Refunds</span>
</label>
</li>
<li className="py-1 px-3">
<label className="flex items-center">
<input type="checkbox" className="form-checkbox" />
<span className="text-sm font-medium ml-2">Last Order</span>
</label>
</li>
<li className="py-1 px-3">
<label className="flex items-center">
<input type="checkbox" className="form-checkbox" />
<span className="text-sm font-medium ml-2">Total Spent</span>
</label>
</li>
</ul>
<div className="py-2 px-3 border-t border-slate-200 bg-slate-50">
<ul className="flex items-center justify-between">
<li>
<button className="btn-xs bg-white border-slate-200 hover:border-slate-300 text-slate-500 hover:text-slate-600">Clear</button>
</li>
<li>
<button className="btn-xs bg-indigo-500 hover:bg-indigo-600 text-white" onClick={() => setDropdownOpen(false)} onBlur={() => setDropdownOpen(false)}>Apply</button>
</li>
</ul>
</div>
</div>
</Transition>
</div>
);
}
export default DropdownFilter;

View File

@@ -1,111 +0,0 @@
import React, { useState, useRef, useEffect } from 'react';
import { Link } from 'react-router-dom';
import Transition from '../utils/Transition';
function DropdownHelp({
align
}) {
const [dropdownOpen, setDropdownOpen] = useState(false);
const trigger = useRef(null);
const dropdown = useRef(null);
// close on click outside
useEffect(() => {
const clickHandler = ({ target }) => {
if (!dropdown.current) return;
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
setDropdownOpen(false);
};
document.addEventListener('click', clickHandler);
return () => document.removeEventListener('click', clickHandler);
});
// close if the esc key is pressed
useEffect(() => {
const keyHandler = ({ keyCode }) => {
if (!dropdownOpen || keyCode !== 27) return;
setDropdownOpen(false);
};
document.addEventListener('keydown', keyHandler);
return () => document.removeEventListener('keydown', keyHandler);
});
return (
<div className="relative inline-flex">
<button
ref={trigger}
className={`w-8 h-8 flex items-center justify-center bg-slate-100 hover:bg-slate-200 transition duration-150 rounded-full ${dropdownOpen && 'bg-slate-200'}`}
aria-haspopup="true"
onClick={() => setDropdownOpen(!dropdownOpen)}
aria-expanded={dropdownOpen}
>
<span className="sr-only">Need help?</span>
<svg className="w-4 h-4" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path className="fill-current text-slate-500" d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
</svg>
</button>
<Transition
className={`origin-top-right z-10 absolute top-full min-w-44 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`}
show={dropdownOpen}
enter="transition ease-out duration-200 transform"
enterStart="opacity-0 -translate-y-2"
enterEnd="opacity-100 translate-y-0"
leave="transition ease-out duration-200"
leaveStart="opacity-100"
leaveEnd="opacity-0"
>
<div
ref={dropdown}
onFocus={() => setDropdownOpen(true)}
onBlur={() => setDropdownOpen(false)}
>
<div className="text-xs font-semibold text-slate-400 uppercase pt-1.5 pb-2 px-4">Need help?</div>
<ul>
<li>
<Link
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
to="#0"
onClick={() => setDropdownOpen(!dropdownOpen)}
>
<svg className="w-3 h-3 fill-current text-indigo-300 shrink-0 mr-2" viewBox="0 0 12 12">
<rect y="3" width="12" height="9" rx="1" />
<path d="M2 0h8v2H2z" />
</svg>
<span>Documentation</span>
</Link>
</li>
<li>
<Link
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
to="#0"
onClick={() => setDropdownOpen(!dropdownOpen)}
>
<svg className="w-3 h-3 fill-current text-indigo-300 shrink-0 mr-2" viewBox="0 0 12 12">
<path d="M10.5 0h-9A1.5 1.5 0 000 1.5v9A1.5 1.5 0 001.5 12h9a1.5 1.5 0 001.5-1.5v-9A1.5 1.5 0 0010.5 0zM10 7L8.207 5.207l-3 3-1.414-1.414 3-3L5 2h5v5z" />
</svg>
<span>Support Site</span>
</Link>
</li>
<li>
<Link
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
to="#0"
onClick={() => setDropdownOpen(!dropdownOpen)}
>
<svg className="w-3 h-3 fill-current text-indigo-300 shrink-0 mr-2" viewBox="0 0 12 12">
<path d="M11.854.146a.5.5 0 00-.525-.116l-11 4a.5.5 0 00-.015.934l4.8 1.921 1.921 4.8A.5.5 0 007.5 12h.008a.5.5 0 00.462-.329l4-11a.5.5 0 00-.116-.525z" />
</svg>
<span>Contact us</span>
</Link>
</li>
</ul>
</div>
</Transition>
</div>
)
}
export default DropdownHelp;

View File

@@ -1,106 +0,0 @@
import React, { useState, useRef, useEffect } from 'react';
import { Link } from 'react-router-dom';
import Transition from '../utils/Transition';
function DropdownNotifications({
align
}) {
const [dropdownOpen, setDropdownOpen] = useState(false);
const trigger = useRef(null);
const dropdown = useRef(null);
// close on click outside
useEffect(() => {
const clickHandler = ({ target }) => {
if (!dropdown.current) return;
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return;
setDropdownOpen(false);
};
document.addEventListener('click', clickHandler);
return () => document.removeEventListener('click', clickHandler);
});
// close if the esc key is pressed
useEffect(() => {
const keyHandler = ({ keyCode }) => {
if (!dropdownOpen || keyCode !== 27) return;
setDropdownOpen(false);
};
document.addEventListener('keydown', keyHandler);
return () => document.removeEventListener('keydown', keyHandler);
});
return (
<div className="relative inline-flex">
<button
ref={trigger}
className={`w-8 h-8 flex items-center justify-center bg-slate-100 hover:bg-slate-200 transition duration-150 rounded-full ${dropdownOpen && 'bg-slate-200'}`}
aria-haspopup="true"
onClick={() => setDropdownOpen(!dropdownOpen)}
aria-expanded={dropdownOpen}
>
<span className="sr-only">Notifications</span>
<svg className="w-4 h-4" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path className="fill-current text-slate-500" d="M6.5 0C2.91 0 0 2.462 0 5.5c0 1.075.37 2.074 1 2.922V12l2.699-1.542A7.454 7.454 0 006.5 11c3.59 0 6.5-2.462 6.5-5.5S10.09 0 6.5 0z" />
<path className="fill-current text-slate-400" d="M16 9.5c0-.987-.429-1.897-1.147-2.639C14.124 10.348 10.66 13 6.5 13c-.103 0-.202-.018-.305-.021C7.231 13.617 8.556 14 10 14c.449 0 .886-.04 1.307-.11L15 16v-4h-.012C15.627 11.285 16 10.425 16 9.5z" />
</svg>
<div className="absolute top-0 right-0 w-2.5 h-2.5 bg-rose-500 border-2 border-white rounded-full"></div>
</button>
<Transition
className={`origin-top-right z-10 absolute top-full -mr-48 sm:mr-0 min-w-80 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`}
show={dropdownOpen}
enter="transition ease-out duration-200 transform"
enterStart="opacity-0 -translate-y-2"
enterEnd="opacity-100 translate-y-0"
leave="transition ease-out duration-200"
leaveStart="opacity-100"
leaveEnd="opacity-0"
>
<div
ref={dropdown}
onFocus={() => setDropdownOpen(true)}
onBlur={() => setDropdownOpen(false)}
>
<div className="text-xs font-semibold text-slate-400 uppercase pt-1.5 pb-2 px-4">Notifications</div>
<ul>
<li className="border-b border-slate-200 last:border-0">
<Link
className="block py-2 px-4 hover:bg-slate-50"
to="#0"
onClick={() => setDropdownOpen(!dropdownOpen)}
>
<span className="block text-sm mb-2">📣 <span className="font-medium text-slate-800">Edit your information in a swipe</span> Sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim.</span>
<span className="block text-xs font-medium text-slate-400">Feb 12, 2021</span>
</Link>
</li>
<li className="border-b border-slate-200 last:border-0">
<Link
className="block py-2 px-4 hover:bg-slate-50"
to="#0"
onClick={() => setDropdownOpen(!dropdownOpen)}
>
<span className="block text-sm mb-2">📣 <span className="font-medium text-slate-800">Edit your information in a swipe</span> Sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim.</span>
<span className="block text-xs font-medium text-slate-400">Feb 9, 2021</span>
</Link>
</li>
<li className="border-b border-slate-200 last:border-0">
<Link
className="block py-2 px-4 hover:bg-slate-50"
to="#0"
onClick={() => setDropdownOpen(!dropdownOpen)}
>
<span className="block text-sm mb-2">🚀<span className="font-medium text-slate-800">Say goodbye to paper receipts!</span> Sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim.</span>
<span className="block text-xs font-medium text-slate-400">Jan 24, 2020</span>
</Link>
</li>
</ul>
</div>
</Transition>
</div>
)
}
export default DropdownNotifications;

View File

@@ -1,15 +1,12 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import {observer} from "mobx-react-lite"; import { observer } from 'mobx-react-lite';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Transition from '../utils/Transition'; import Transition from '../utils/Transition';
import {registrationStore} from "../store/store"; import { store } from '../store/store';
import {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg"; import { generateSvgAvatar } from '../images/GenerateOnboardingSvg/GenerateSvg';
import UserAvatar from '../images/user-avatar-32.png'; import UserAvatar from '../images/user-avatar-32.png';
const DropdownProfile = observer(({ const DropdownProfile = observer(({ align }) => {
align
})=>{
const [dropdownOpen, setDropdownOpen] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false);
const trigger = useRef(null); const trigger = useRef(null);
@@ -19,7 +16,12 @@ import UserAvatar from '../images/user-avatar-32.png';
useEffect(() => { useEffect(() => {
const clickHandler = ({ target }) => { const clickHandler = ({ target }) => {
if (!dropdown.current) return; if (!dropdown.current) return;
if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return; if (
!dropdownOpen ||
dropdown.current.contains(target) ||
trigger.current.contains(target)
)
return;
setDropdownOpen(false); setDropdownOpen(false);
}; };
document.addEventListener('click', clickHandler); document.addEventListener('click', clickHandler);
@@ -36,6 +38,11 @@ import UserAvatar from '../images/user-avatar-32.png';
return () => document.removeEventListener('keydown', keyHandler); return () => document.removeEventListener('keydown', keyHandler);
}); });
const logOut = () => {
sessionStorage.removeItem('passPhrase');
store.fetchPassPhrase();
};
return ( return (
<div className="relative inline-flex"> <div className="relative inline-flex">
<button <button
@@ -45,17 +52,30 @@ import UserAvatar from '../images/user-avatar-32.png';
onClick={() => setDropdownOpen(!dropdownOpen)} onClick={() => setDropdownOpen(!dropdownOpen)}
aria-expanded={dropdownOpen} aria-expanded={dropdownOpen}
> >
<img className="w-8 h-8 rounded-full" src={generateSvgAvatar(registrationStore.pubKey) || UserAvatar} width="32" height="32" alt="User" /> <img
className="w-8 h-8 rounded-full"
src={generateSvgAvatar(store.pubKey) || UserAvatar}
width="32"
height="32"
alt="User"
/>
<div className="flex items-center truncate"> <div className="flex items-center truncate">
<span className="truncate ml-2 text-sm font-medium group-hover:text-slate-800">Acme Inc.</span> <span className="truncate ml-2 text-sm font-medium group-hover:text-slate-800">
<svg className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400" viewBox="0 0 12 12"> {store.accountName}
</span>
<svg
className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400"
viewBox="0 0 12 12"
>
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" /> <path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
</svg> </svg>
</div> </div>
</button> </button>
<Transition <Transition
className={`origin-top-right z-10 absolute top-full min-w-44 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${align === 'right' ? 'right-0' : 'left-0'}`} className={`origin-top-right z-10 absolute top-full min-w-44 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${
align === 'right' ? 'right-0' : 'left-0'
}`}
show={dropdownOpen} show={dropdownOpen}
enter="transition ease-out duration-200 transform" enter="transition ease-out duration-200 transform"
enterStart="opacity-0 -translate-y-2" enterStart="opacity-0 -translate-y-2"
@@ -70,24 +90,24 @@ import UserAvatar from '../images/user-avatar-32.png';
onBlur={() => setDropdownOpen(false)} onBlur={() => setDropdownOpen(false)}
> >
<div className="pt-0.5 pb-2 px-3 mb-1 border-b border-slate-200"> <div className="pt-0.5 pb-2 px-3 mb-1 border-b border-slate-200">
<div className="font-medium text-slate-800">Acme Inc.</div> <div className="font-medium text-slate-800">
<div className="text-xs text-slate-500 italic">Administrator</div> {store.accountName}
</div>
</div> </div>
<ul> <ul>
<li> <li>
<Link <span
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3" className="cursor-pointer font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
to="/dashboard"
onClick={() => setDropdownOpen(!dropdownOpen)} onClick={() => setDropdownOpen(!dropdownOpen)}
> >
Settings Settings
</Link> </span>
</li> </li>
<li> <li>
<Link <Link
className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3" className="font-medium text-sm text-indigo-500 hover:text-indigo-600 flex items-center py-1 px-3"
to="/dashboard" to="/"
onClick={() => setDropdownOpen(!dropdownOpen)} onClick={() => logOut()}
> >
Sign Out Sign Out
</Link> </Link>
@@ -96,7 +116,7 @@ import UserAvatar from '../images/user-avatar-32.png';
</div> </div>
</Transition> </Transition>
</div> </div>
) );
}) });
export default DropdownProfile; export default DropdownProfile;

View File

@@ -0,0 +1,104 @@
import React, { useState, useRef, useEffect } from 'react';
import Transition from '../utils/Transition';
function DropdownTransaction({ align, value, onChange }) {
const [dropdownOpen, setDropdownOpen] = useState(false);
const trigger = useRef(null);
const dropdown = useRef(null);
const changeValue = (val) => {
setDropdownOpen(false);
onChange(val);
};
// close on click outside
useEffect(() => {
const clickHandler = ({ target }) => {
if (!dropdown.current) return;
if (
!dropdownOpen ||
dropdown.current.contains(target) ||
trigger.current.contains(target)
)
return;
setDropdownOpen(false);
};
document.addEventListener('click', clickHandler);
return () => document.removeEventListener('click', clickHandler);
});
// close if the esc key is pressed
useEffect(() => {
const keyHandler = ({ keyCode }) => {
if (!dropdownOpen || keyCode !== 27) return;
setDropdownOpen(false);
};
document.addEventListener('keydown', keyHandler);
return () => document.removeEventListener('keydown', keyHandler);
});
return (
<div className="relative inline-flex">
<button
ref={trigger}
className="inline-flex justify-center items-center group"
aria-haspopup="true"
aria-expanded={dropdownOpen}
onClick={() => setDropdownOpen(!dropdownOpen)}
>
<div className="flex items-center truncate">
<span className="truncate font-medium text-indigo-500 group-hover:text-indigo-600">
{value}
</span>
<svg
className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400"
viewBox="0 0 12 12"
>
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
</svg>
</div>
</button>
<Transition
className={`origin-top-right z-10 absolute top-full min-w-44 bg-white border border-slate-200 py-1.5 rounded shadow-lg overflow-hidden mt-1 ${
align === 'right' ? 'right-0' : 'left-0'
}`}
show={dropdownOpen}
enter="transition ease-out duration-200 transform"
enterStart="opacity-0 -translate-y-2"
enterEnd="opacity-100 translate-y-0"
leave="transition ease-out duration-200"
leaveStart="opacity-100"
leaveEnd="opacity-0"
>
<div
ref={dropdown}
onFocus={() => setDropdownOpen(true)}
onBlur={() => setDropdownOpen(false)}
>
<ul>
<li>
<span
className="font-medium text-sm text-slate-600 hover:text-slate-800 flex items-center py-1 px-3 cursor-pointer"
onClick={() => changeValue('Voiting')}
>
Voiting
</span>
</li>
<li>
<span
className="font-medium text-sm text-slate-600 hover:text-slate-800 flex items-center py-1 px-3 cursor-pointer"
onClick={() => changeValue('Unlocking')}
>
Unlocking
</span>
</li>
</ul>
</div>
</Transition>
</div>
);
}
export default DropdownTransaction;

31
src/components/Filters.js Normal file
View File

@@ -0,0 +1,31 @@
import React from 'react';
export const Filters = ({ value, onChange, values = [] }) => {
return (
<div className="mb-5">
<ul className="flex flex-wrap -m-1">
{values.map((e) =>
e === value ? (
<li className="m-1" key={e}>
<button
className="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-transparent shadow-sm bg-indigo-500 text-white duration-150 ease-in-out"
onClick={() => onChange(e)}
>
{e}
</button>
</li>
) : (
<li className="m-1" key={e}>
<button
className="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-slate-200 hover:border-slate-300 shadow-sm bg-white text-slate-500 duration-150 ease-in-out"
onClick={() => onChange(e)}
>
{e}
</button>
</li>
)
)}
</ul>
</div>
);
};

View File

@@ -0,0 +1,17 @@
import ReactDOM from 'react-dom';
import React from 'react';
import { ThreeCircles } from 'react-loader-spinner';
export const LoadingOverlay = () =>
ReactDOM.createPortal(
<div className="absolute left-0 top-0 h-screen w-screen z-50 loading-overlay">
<ThreeCircles
height="100"
width="100"
color="#6366f1"
visible={true}
ariaLabel="three-circles-rotating"
/>
</div>,
document.body
);

View File

@@ -1,197 +0,0 @@
import React, { useRef, useEffect } from 'react';
import { Link } from 'react-router-dom';
import Transition from '../utils/Transition';
function ModalSearch({
id,
searchId,
modalOpen,
setModalOpen
}) {
const modalContent = useRef(null);
const searchInput = useRef(null);
// close on click outside
useEffect(() => {
const clickHandler = ({ target }) => {
if (!modalOpen || modalContent.current.contains(target)) return
setModalOpen(false);
};
document.addEventListener('click', clickHandler);
return () => document.removeEventListener('click', clickHandler);
});
// close if the esc key is pressed
useEffect(() => {
const keyHandler = ({ keyCode }) => {
if (!modalOpen || keyCode !== 27) return;
setModalOpen(false);
};
document.addEventListener('keydown', keyHandler);
return () => document.removeEventListener('keydown', keyHandler);
});
useEffect(() => {
modalOpen && searchInput.current.focus();
}, [modalOpen]);
return (
<>
{/* Modal backdrop */}
<Transition
className="fixed inset-0 bg-slate-900 bg-opacity-30 z-50 transition-opacity"
show={modalOpen}
enter="transition ease-out duration-200"
enterStart="opacity-0"
enterEnd="opacity-100"
leave="transition ease-out duration-100"
leaveStart="opacity-100"
leaveEnd="opacity-0"
aria-hidden="true"
/>
{/* Modal dialog */}
<Transition
id={id}
className="fixed inset-0 z-50 overflow-hidden flex items-start top-20 mb-4 justify-center transform px-4 sm:px-6"
role="dialog"
aria-modal="true"
show={modalOpen}
enter="transition ease-in-out duration-200"
enterStart="opacity-0 translate-y-4"
enterEnd="opacity-100 translate-y-0"
leave="transition ease-in-out duration-200"
leaveStart="opacity-100 translate-y-0"
leaveEnd="opacity-0 translate-y-4"
>
<div ref={modalContent} className="bg-white overflow-auto max-w-2xl w-full max-h-full rounded shadow-lg">
{/* Search form */}
<form className="border-b border-slate-200">
<div className="relative">
<label htmlFor={searchId} className="sr-only">Search</label>
<input id={searchId} className="w-full border-0 focus:ring-transparent placeholder-slate-400 appearance-none py-3 pl-10 pr-4" type="search" placeholder="Search Anything…" ref={searchInput} />
<button className="absolute inset-0 right-auto group" type="submit" aria-label="Search">
<svg className="w-4 h-4 shrink-0 fill-current text-slate-400 group-hover:text-slate-500 ml-4 mr-2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M7 14c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7zM7 2C4.243 2 2 4.243 2 7s2.243 5 5 5 5-2.243 5-5-2.243-5-5-5z" />
<path d="M15.707 14.293L13.314 11.9a8.019 8.019 0 01-1.414 1.414l2.393 2.393a.997.997 0 001.414 0 .999.999 0 000-1.414z" />
</svg>
</button>
</div>
</form>
<div className="py-4 px-2">
{/* Recent searches */}
<div className="mb-3 last:mb-0">
<div className="text-xs font-semibold text-slate-400 uppercase px-2 mb-2">Recent searches</div>
<ul className="text-sm">
<li>
<Link
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
to="#0"
onClick={() => setModalOpen(!modalOpen)}
>
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
</svg>
<span>Form Builder - 23 hours on-demand video</span>
</Link>
</li>
<li>
<Link
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
to="#0"
onClick={() => setModalOpen(!modalOpen)}
>
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
</svg>
<span>Access Mosaic on mobile and TV</span>
</Link>
</li>
<li>
<Link
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
to="#0"
onClick={() => setModalOpen(!modalOpen)}
>
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
</svg>
<span>Product Update - Q4 2021</span>
</Link>
</li>
<li>
<Link
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
to="#0"
onClick={() => setModalOpen(!modalOpen)}
>
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
</svg>
<span>Master Digital Marketing Strategy course</span>
</Link>
</li>
<li>
<Link
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
to="#0"
onClick={() => setModalOpen(!modalOpen)}
>
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
</svg>
<span>Dedicated forms for products</span>
</Link>
</li>
<li>
<Link
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
to="#0"
onClick={() => setModalOpen(!modalOpen)}
>
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
<path d="M15.707 14.293v.001a1 1 0 01-1.414 1.414L11.185 12.6A6.935 6.935 0 017 14a7.016 7.016 0 01-5.173-2.308l-1.537 1.3L0 8l4.873 1.12-1.521 1.285a4.971 4.971 0 008.59-2.835l1.979.454a6.971 6.971 0 01-1.321 3.157l3.107 3.112zM14 6L9.127 4.88l1.521-1.28a4.971 4.971 0 00-8.59 2.83L.084 5.976a6.977 6.977 0 0112.089-3.668l1.537-1.3L14 6z" />
</svg>
<span>Product Update - Q4 2021</span>
</Link>
</li>
</ul>
</div>
{/* Recent pages */}
<div className="mb-3 last:mb-0">
<div className="text-xs font-semibold text-slate-400 uppercase px-2 mb-2">Recent pages</div>
<ul className="text-sm">
<li>
<Link
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
to="#0"
onClick={() => setModalOpen(!modalOpen)}
>
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
<path d="M14 0H2c-.6 0-1 .4-1 1v14c0 .6.4 1 1 1h8l5-5V1c0-.6-.4-1-1-1zM3 2h10v8H9v4H3V2z" />
</svg>
<span><span className="font-medium text-slate-800 group-hover:text-white">Messages</span> - Conversation / / Mike Mills</span>
</Link>
</li>
<li>
<Link
className="flex items-center p-2 text-slate-800 hover:text-white hover:bg-indigo-500 rounded group"
to="#0"
onClick={() => setModalOpen(!modalOpen)}
>
<svg className="w-4 h-4 fill-current text-slate-400 group-hover:text-white group-hover:text-opacity-50 shrink-0 mr-3" viewBox="0 0 16 16">
<path d="M14 0H2c-.6 0-1 .4-1 1v14c0 .6.4 1 1 1h8l5-5V1c0-.6-.4-1-1-1zM3 2h10v8H9v4H3V2z" />
</svg>
<span><span className="font-medium text-slate-800 group-hover:text-white">Messages</span> - Conversation / / Eva Patrick</span>
</Link>
</li>
</ul>
</div>
</div>
</div>
</Transition>
</>
);
}
export default ModalSearch;

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import { Toast } from './Toast2';
import { store } from '../store/store';
export const NotificationContainer = observer(() => (
<div className="absolute z-50 top-2 right-2 gap-2">
{store.notifications.map((e) => (
<Toast type={e.type} key={e.id} id={e.id}>
{e.message}
</Toast>
))}
</div>
));

View File

@@ -0,0 +1,52 @@
import React from 'react';
import cn from 'classnames';
function PaginationClassic({ count, limit, offset, onNextPage, onPrevPage }) {
return (
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between">
<nav
className="mb-4 sm:mb-0 sm:order-1"
role="navigation"
aria-label="Navigation"
>
<ul className="flex justify-center">
<li className="ml-3 first:ml-0">
<button
className={cn(
'btn bg-white border-slate-200 text-indigo-500',
{ 'cursor-not-allowed text-slate-300': offset === 0 },
{ 'hover:border-slate-300': offset !== 0 }
)}
disabled={offset === 0}
onClick={onPrevPage}
>
&lt;- Previous
</button>
</li>
<li className="ml-3 first:ml-0">
<button
className={cn(
'btn bg-white border-slate-200 text-indigo-500',
{
'cursor-not-allowed text-slate-300': offset + limit >= count,
},
{ 'hover:border-slate-300': offset + limit < count }
)}
disabled={offset + limit >= count}
onClick={onNextPage}
>
Next -&gt;
</button>
</li>
</ul>
</nav>
<div className="text-sm text-slate-500 text-center sm:text-left">
Showing <span className="font-medium text-slate-600">{offset + 1}</span>{' '}
to <span className="font-medium text-slate-600">{offset + limit}</span>{' '}
of <span className="font-medium text-slate-600">{count}</span> results
</div>
</div>
);
}
export default PaginationClassic;

90
src/components/Toast2.jsx Normal file
View File

@@ -0,0 +1,90 @@
import React, { useEffect } from 'react';
import { store } from '../store/store';
import { observer } from 'mobx-react-lite';
export const Toast = observer(({ children, className, type, id }) => {
const typeIcon = (type) => {
switch (type) {
case 'warning':
return (
<svg
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
viewBox="0 0 16 16"
>
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
</svg>
);
case 'error':
return (
<svg
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
viewBox="0 0 16 16"
>
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3.5 10.1l-1.4 1.4L8 9.4l-2.1 2.1-1.4-1.4L6.6 8 4.5 5.9l1.4-1.4L8 6.6l2.1-2.1 1.4 1.4L9.4 8l2.1 2.1z" />
</svg>
);
case 'success':
return (
<svg
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
viewBox="0 0 16 16"
>
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zM7 11.4L3.6 8 5 6.6l2 2 4-4L12.4 6 7 11.4z" />
</svg>
);
default:
return (
<svg
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
viewBox="0 0 16 16"
>
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm1 12H7V7h2v5zM8 6c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z" />
</svg>
);
}
};
const typeColor = (type) => {
switch (type) {
case 'warning':
return 'bg-amber-100 border-amber-200 text-amber-600';
case 'error':
return 'bg-rose-100 border-rose-200 text-rose-600';
case 'success':
return 'bg-emerald-100 border-emerald-200 text-emerald-600';
default:
return 'bg-indigo-100 border-indigo-200 text-indigo-500';
}
};
useEffect(() => {
const timerId = setTimeout(() => store.removeNotification(id), 3000);
return () => clearTimeout(timerId);
}, []);
return (
<div className={className}>
<div
className={`inline-flex min-w-80 px-4 py-2 rounded-sm text-sm border ${typeColor(
type
)}`}
>
<div className="flex w-full justify-between items-start">
<div className="flex">
{typeIcon(type)}
<div>{children}</div>
</div>
<button
className="opacity-70 hover:opacity-80 ml-3 mt-[3px]"
onClick={() => store.removeNotification(id)}
>
<div className="sr-only">Close</div>
<svg className="w-4 h-4 fill-current">
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
</svg>
</button>
</div>
</div>
</div>
);
});

View File

@@ -1,90 +0,0 @@
import React, { useState } from 'react';
import Transition from '../utils/Transition';
function Tooltip({
children,
className,
bg,
size,
position,
}) {
const [tooltipOpen, setTooltipOpen] = useState(false);
const positionOuterClasses = (position) => {
switch (position) {
case 'right':
return 'left-full top-1/2 transform -translate-y-1/2';
case 'left':
return 'right-full top-1/2 transform -translate-y-1/2';
case 'bottom':
return 'top-full left-1/2 transform -translate-x-1/2';
default:
return 'bottom-full left-1/2 transform -translate-x-1/2';
}
}
const sizeClasses = (size) => {
switch (size) {
case 'lg':
return 'min-w-72 p-3';
case 'md':
return 'min-w-56 p-3';
case 'sm':
return 'min-w-44 p-2';
default:
return 'p-2';
}
};
const positionInnerClasses = (position) => {
switch (position) {
case 'right':
return 'ml-2';
case 'left':
return 'mr-2';
case 'bottom':
return 'mt-2';
default:
return 'mb-2';
}
};
return (
<div
className={`relative ${className}`}
onMouseEnter={() => setTooltipOpen(true)}
onMouseLeave={() => setTooltipOpen(false)}
onFocus={() => setTooltipOpen(true)}
onBlur={() => setTooltipOpen(false)}
>
<button
className="block"
aria-haspopup="true"
aria-expanded={tooltipOpen}
onClick={(e) => e.preventDefault()}
>
<svg className="w-4 h-4 fill-current text-slate-400" viewBox="0 0 16 16">
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
</svg>
</button>
<div className={`z-10 absolute ${positionOuterClasses(position)}`}>
<Transition
show={tooltipOpen}
tag="div"
className={`rounded overflow-hidden ${bg === 'dark' ? 'bg-slate-800' : 'bg-white border border-slate-200 shadow-lg'} ${sizeClasses(size)} ${positionInnerClasses(position)}`}
enter="transition ease-out duration-200 transform"
enterStart="opacity-0 -translate-y-2"
enterEnd="opacity-100 translate-y-0"
leave="transition ease-out duration-200"
leaveStart="opacity-100"
leaveEnd="opacity-0"
>
{children}
</Transition>
</div>
</div>
);
}
export default Tooltip;

View File

@@ -4,7 +4,7 @@
// Customise flatpickr // Customise flatpickr
$calendarPadding: 24px; $calendarPadding: 24px;
$daySize: 36px; $daySize: 36px;
$daysWidth: $daySize*7; $daysWidth: $daySize * 7;
@keyframes fpFadeInDown { @keyframes fpFadeInDown {
from { from {
@@ -20,9 +20,9 @@ $daysWidth: $daySize*7;
.flatpickr-calendar { .flatpickr-calendar {
border: inherit; border: inherit;
@apply rounded shadow-lg border border-slate-200 left-1/2; @apply rounded shadow-lg border border-slate-200 left-1/2;
margin-left: - ($daysWidth + $calendarPadding*2)*0.5; margin-left: -($daysWidth + $calendarPadding * 2) * 0.5;
padding: $calendarPadding; padding: $calendarPadding;
width: $daysWidth + $calendarPadding*2; width: $daysWidth + $calendarPadding * 2;
} }
@screen lg { @screen lg {
@@ -141,9 +141,9 @@ $daysWidth: $daySize*7;
} }
.flatpickr-day.inRange, .flatpickr-day.inRange,
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)), .flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n + 1)),
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)), .flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n + 1)),
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) { .flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n + 1)) {
box-shadow: none; box-shadow: none;
} }

View File

@@ -1,7 +1,7 @@
// Range slider // Range slider
$range-thumb-size: 36px; $range-thumb-size: 36px;
input[type=range] { input[type='range'] {
appearance: none; appearance: none;
background: #ccc; background: #ccc;
border-radius: 3px; border-radius: 3px;

View File

@@ -4,8 +4,14 @@
.form-select, .form-select,
.form-checkbox, .form-checkbox,
.form-radio { .form-radio {
&:focus { &:focus {
@apply ring-0; @apply ring-0;
} }
} }
.loading-overlay {
background-color: rgba(0, 0, 0, 0.25);
display: flex;
justify-content: center;
align-items: center;
}

View File

@@ -13,14 +13,12 @@
top: 2px; top: 2px;
left: 2px; left: 2px;
right: 50%; right: 50%;
transition: all .15s ease-out; transition: all 0.15s ease-out;
} }
} }
input[type="checkbox"] { input[type='checkbox'] {
&:checked { &:checked {
+ label { + label {
@apply bg-indigo-500; @apply bg-indigo-500;
@@ -31,7 +29,6 @@
} }
&:disabled { &:disabled {
+ label { + label {
@apply cursor-not-allowed bg-slate-100 border border-slate-200; @apply cursor-not-allowed bg-slate-100 border border-slate-200;

View File

@@ -50,10 +50,10 @@
} }
// Forms // Forms
input[type="search"]::-webkit-search-decoration, input[type='search']::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button, input[type='search']::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button, input[type='search']::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration { input[type='search']::-webkit-search-results-decoration {
-webkit-appearance: none; -webkit-appearance: none;
} }

View File

@@ -1,21 +1,20 @@
function createBlob(options) { function createBlob(options) {
let points = []; let points = [];
let slice = (Math.PI * 2) / options.numPoints; let slice = (Math.PI * 2) / options.numPoints;
let startAngle = 0; let startAngle = 0;
for (let i = 0; i < options.subkey.length; i++) { for (let i = 0; i < options.subkey.length; i++) {
let angle = startAngle + i * slice; let angle = startAngle + i * slice;
let rnd = Number("0x"+options.subkey.substring(i+0,i+1)); let rnd = Number('0x' + options.subkey.substring(i + 0, i + 1));
let point = { let point = {
x: options.centerX + Math.cos(angle) * (options.radius + rnd), x: options.centerX + Math.cos(angle) * (options.radius + rnd),
y: options.centerY + Math.sin(angle) * (options.radius + rnd) y: options.centerY + Math.sin(angle) * (options.radius + rnd),
}; };
points.push(point); points.push(point);
} }
let size = points.length; let size = points.length;
let path = "M" + points[0].x + " " + points[0].y + " C"; let path = 'M' + points[0].x + ' ' + points[0].y + ' C';
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
let p0 = points[(i - 1 + size) % size]; let p0 = points[(i - 1 + size) % size];
@@ -29,45 +28,64 @@ function createBlob(options) {
let x2 = p2.x - (p3.x - p1.x) / 6; let x2 = p2.x - (p3.x - p1.x) / 6;
let y2 = p2.y - (p3.y - p1.y) / 6; let y2 = p2.y - (p3.y - p1.y) / 6;
path += " " + x1 + " " + y1 + " " + x2 + " " + y2 + " " + p2.x + " " + p2.y; path += ' ' + x1 + ' ' + y1 + ' ' + x2 + ' ' + y2 + ' ' + p2.x + ' ' + p2.y;
} }
return path + "z"; return path + 'z';
} }
export var generateSvgAvatar = ( function () { export var generateSvgAvatar = (function () {
return function (publicKey, seed, raw) { return function (publicKey, seed, raw) {
let pubKey = publicKey || Math.floor(Math.random()*65535*65535).toString(16) + Math.floor(Math.random()*65535*65535).toString(16) + Math.floor(Math.random()*65535*65535).toString(16) + Math.floor(Math.random()*65535*65535).toString(16) + Math.floor(Math.random()*65535*65535).toString(16) + Math.floor(Math.random()*65535*65535).toString(16) + Math.floor(Math.random()*65535*65535).toString(16) + Math.floor(Math.random()*65535*65535).toString(16); let pubKey =
publicKey ||
Math.floor(Math.random() * 65535 * 65535).toString(16) +
Math.floor(Math.random() * 65535 * 65535).toString(16) +
Math.floor(Math.random() * 65535 * 65535).toString(16) +
Math.floor(Math.random() * 65535 * 65535).toString(16) +
Math.floor(Math.random() * 65535 * 65535).toString(16) +
Math.floor(Math.random() * 65535 * 65535).toString(16) +
Math.floor(Math.random() * 65535 * 65535).toString(16) +
Math.floor(Math.random() * 65535 * 65535).toString(16);
//pubKey = "73f47d4929d7b2b6598ca4999d52567f9bb5aef3e353e0a66b468096db6e4e88" //pubKey = "73f47d4929d7b2b6598ca4999d52567f9bb5aef3e353e0a66b468096db6e4e88"
let particles = []; let particles = [];
let gradients = ['#000000']; let gradients = ['#000000'];
let defs = []; let defs = [];
for (let a = 0; a<64; a = a + 8) { for (let a = 0; a < 64; a = a + 8) {
let blob = createBlob({ let blob = createBlob({
numPoints: 8, numPoints: 8,
centerX: 256, centerX: 256,
centerY: 256-a*4, centerY: 256 - a * 4,
radius: 16, radius: 16,
subkey: pubKey.substring(a,a + 8) subkey: pubKey.substring(a, a + 8),
}); });
let color = "#" + pubKey.substring(a+2,a+8); let color = '#' + pubKey.substring(a + 2, a + 8);
let width = Number("0x"+pubKey.substring(a,a+1)); let width = Number('0x' + pubKey.substring(a, a + 1));
let opacity = Number("0x"+pubKey.substring(a+1,a+2))/8; let opacity = Number('0x' + pubKey.substring(a + 1, a + 2)) / 8;
gradients.push(color); gradients.push(color);
defs.push(`<radialGradient id="a-${a}" cx="0" cy="0" r="0.75" fx="0.35" fy="0.5" spreadMethod="reflect"><stop offset="0%" stop-color="${gradients[gradients.length-1]}"/><stop offset="100%" stop-color="${gradients[gradients.length]}"/></radialGradient>`); defs.push(
particles.push(`<path d="${blob}" stroke="url(#a-${a})" stroke-width="${width}" stroke-opacity = "${opacity}" fill="transparent"/>`); `<radialGradient id="a-${a}" cx="0" cy="0" r="0.75" fx="0.35" fy="0.5" spreadMethod="reflect"><stop offset="0%" stop-color="${
gradients[gradients.length - 1]
}"/><stop offset="100%" stop-color="${
gradients[gradients.length]
}"/></radialGradient>`
);
particles.push(
`<path d="${blob}" stroke="url(#a-${a})" stroke-width="${width}" stroke-opacity = "${opacity}" fill="transparent"/>`
);
} }
let ph = ""; let ph = '';
let off = 4 + Number("0x"+pubKey.substring(0,1)); let off = 4 + Number('0x' + pubKey.substring(0, 1));
let rotate = 0; let rotate = 0;
for (var i = 0; i < off; i++){ for (var i = 0; i < off; i++) {
ph = ph + `<use xlink:href="#pattern-${pubKey}" mask="url(#mask)" class="segment" style="transform-origin:center center;transform: scaleY(1) rotate(${rotate}deg)"></use>,` + "\n"; ph =
rotate = rotate + 360/off; ph +
`<use xlink:href="#pattern-${pubKey}" mask="url(#mask)" class="segment" style="transform-origin:center center;transform: scaleY(1) rotate(${rotate}deg)"></use>,` +
'\n';
rotate = rotate + 360 / off;
} }
var svg = [ var svg = [
@@ -76,17 +94,19 @@ export var generateSvgAvatar = ( function () {
'<radialGradient id="e" cx="0" cy="0" r="0.75" fx="0.35" fy="0.5" spreadMethod="reflect">', '<radialGradient id="e" cx="0" cy="0" r="0.75" fx="0.35" fy="0.5" spreadMethod="reflect">',
'<stop offset="0%" stop-color="red"/>', '<stop offset="0%" stop-color="red"/>',
'<stop offset="100%" stop-color="blue"/>', '<stop offset="100%" stop-color="blue"/>',
'</radialGradient>',defs.join('\n'), '</radialGradient>',
defs.join('\n'),
'</defs>', '</defs>',
'<symbol id="pattern-' + pubKey + '">', '<symbol id="pattern-' + pubKey + '">',
particles.join(''), particles.join(''),
'</symbol>', '</symbol>',
'<g style="transform-origin:center center; transform: rotate(' + pubKey + 'deg)">', '<g style="transform-origin:center center; transform: rotate(' +
pubKey +
'deg)">',
ph, ph,
'</g>', '</g>',
'</svg>', '</svg>',
].join(''); ].join('');
return raw ? svg : 'data:image/svg+xml;base64,' + btoa(svg); return raw ? svg : 'data:image/svg+xml;base64,' + btoa(svg);
}; };
})(); })();

BIN
src/images/service_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

BIN
src/images/vpn_service.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -1,16 +1,29 @@
import React from 'react' import React from 'react';
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom';
import {Buffer} from "buffer"; import { Buffer } from 'buffer';
import { HashRouter as Router } from 'react-router-dom' import { HashRouter as Router } from 'react-router-dom';
import App from './App' import App from './App';
import { store } from './store/store';
import { fetchWrapper } from './shared/fetchWrapper';
globalThis.Buffer = Buffer globalThis.Buffer = Buffer;
ReactDOM.render( window.store = store;
BigInt.prototype.toJSON = function () {
return this.toString();
};
fetch('settings.json')
.then((e) => e.json())
.then((data) => (fetchWrapper.baseUrl = data.API_URL))
.then(() =>
ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<Router> <Router>
<App /> <App />
</Router> </Router>
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById('root')
) )
);

View File

@@ -1,92 +0,0 @@
import React, { useState } from 'react';
import Sidebar from '../partials/Sidebar';
import Header from '../partials/Header';
import Datepicker from '../components/Datepicker';
import AnalyticsCard01 from '../partials/analytics/AnalyticsCard01';
import AnalyticsCard02 from '../partials/analytics/AnalyticsCard02';
import AnalyticsCard03 from '../partials/analytics/AnalyticsCard03';
import AnalyticsCard04 from '../partials/analytics/AnalyticsCard04';
import AnalyticsCard05 from '../partials/analytics/AnalyticsCard05';
import AnalyticsCard06 from '../partials/analytics/AnalyticsCard06';
import AnalyticsCard07 from '../partials/analytics/AnalyticsCard07';
import AnalyticsCard08 from '../partials/analytics/AnalyticsCard08';
import AnalyticsCard09 from '../partials/analytics/AnalyticsCard09';
import AnalyticsCard10 from '../partials/analytics/AnalyticsCard10';
import AnalyticsCard11 from '../partials/analytics/AnalyticsCard11';
function Analytics() {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<div className="flex h-screen overflow-hidden">
{/* Sidebar */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* Content area */}
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden">
{/* Site header */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<main>
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
{/* Page header */}
<div className="sm:flex sm:justify-between sm:items-center mb-8">
{/* Left: Title */}
<div className="mb-4 sm:mb-0">
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">Analytics </h1>
</div>
{/* Right: Actions */}
<div className="grid grid-flow-col sm:auto-cols-max justify-start sm:justify-end gap-2">
{/* Datepicker built with flatpickr */}
<Datepicker align="right" />
</div>
</div>
{/* Cards */}
<div className="grid grid-cols-12 gap-6">
{/* Line chart (Analytics) */}
<AnalyticsCard01 />
{/* Line chart (Active Users Right Now) */}
<AnalyticsCard02 />
{/* Stacked bar chart (Acquisition Channels) */}
<AnalyticsCard03 />
{/* Horizontal bar chart (Audience Overview) */}
<AnalyticsCard04 />
{/* Report card (Top Channels) */}
<AnalyticsCard05 />
{/* Report card (Top Pages) */}
<AnalyticsCard06 />
{/* Report card (Top Countries) */}
<AnalyticsCard07 />
{/* Doughnut chart (Sessions By Device) */}
<AnalyticsCard08 />
{/* Doughnut chart (Visit By Age Category) */}
<AnalyticsCard09 />
{/* Polar chart (Sessions By Gender) */}
<AnalyticsCard10 />
{/* Table (Top Products) */}
<AnalyticsCard11 />
</div>
</div>
</main>
</div>
</div>
);
}
export default Analytics;

View File

@@ -1,102 +0,0 @@
import React, {useState} from 'react';
import Sidebar from '../partials/Sidebar';
import Header from '../partials/Header';
import WelcomeBanner from '../partials/dashboard/WelcomeBanner';
import DashboardAvatars from '../partials/dashboard/DashboardAvatars';
import FilterButton from '../components/DropdownFilter';
import Datepicker from '../components/Datepicker';
import DashboardCard01 from '../partials/dashboard/DashboardCard01';
import DashboardCard02 from '../partials/dashboard/DashboardCard02';
import DashboardCard03 from '../partials/dashboard/DashboardCard03';
import DashboardCard04 from '../partials/dashboard/DashboardCard04';
import DashboardCard05 from '../partials/dashboard/DashboardCard05';
import DashboardCard06 from '../partials/dashboard/DashboardCard06';
import DashboardCard07 from '../partials/dashboard/DashboardCard07';
import DashboardCard08 from '../partials/dashboard/DashboardCard08';
import DashboardCard09 from '../partials/dashboard/DashboardCard09';
import DashboardCard10 from '../partials/dashboard/DashboardCard10';
import DashboardCard11 from '../partials/dashboard/DashboardCard11';
function Dashboard() {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<div className="flex h-screen overflow-hidden">
{/* Sidebar */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* Content area */}
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden">
{/* Site header */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<main>
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
{/* Welcome banner */}
<WelcomeBanner />
{/* Dashboard actions */}
<div className="sm:flex sm:justify-between sm:items-center mb-8">
{/* Left: Avatars */}
<DashboardAvatars />
{/* Right: Actions */}
<div className="grid grid-flow-col sm:auto-cols-max justify-start sm:justify-end gap-2">
{/* Filter button */}
<FilterButton align="right" />
{/* Datepicker built with flatpickr */}
<Datepicker align="right" />
{/* Add view button */}
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white">
<svg className="w-4 h-4 fill-current opacity-50 shrink-0" viewBox="0 0 16 16">
<path d="M15 7H9V1c0-.6-.4-1-1-1S7 .4 7 1v6H1c-.6 0-1 .4-1 1s.4 1 1 1h6v6c0 .6.4 1 1 1s1-.4 1-1V9h6c.6 0 1-.4 1-1s-.4-1-1-1z" />
</svg>
<span className="hidden xs:block ml-2">Add View</span>
</button>
</div>
</div>
{/* Cards */}
<div className="grid grid-cols-12 gap-6">
{/* Line chart (Acme Plus) */}
<DashboardCard01 />
{/* Line chart (Acme Advanced) */}
<DashboardCard02 />
{/* Line chart (Acme Professional) */}
<DashboardCard03 />
{/* Bar chart (Direct vs Indirect) */}
<DashboardCard04 />
{/* Line chart (Real Time Value) */}
<DashboardCard05 />
{/* Doughnut chart (Top Countries) */}
<DashboardCard06 />
{/* Table (Top Channels) */}
<DashboardCard07 />
{/* Line chart (Sales Over Time) */}
<DashboardCard08 />
{/* Stacked bar chart (Sales VS Refunds) */}
<DashboardCard09 />
{/* Card (Recent Activity) */}
<DashboardCard10 />
{/* Card (Income/Expenses) */}
<DashboardCard11 />
</div>
</div>
</main>
</div>
</div>
);
}
export default Dashboard;

View File

@@ -1,104 +0,0 @@
import React, { useState } from 'react';
import Sidebar from '../partials/Sidebar';
import Header from '../partials/Header';
import Datepicker from '../components/Datepicker';
import FintechIntro from '../partials/fintech/FintechIntro';
import FintechCard01 from '../partials/fintech/FintechCard01';
import FintechCard02 from '../partials/fintech/FintechCard02';
import FintechCard03 from '../partials/fintech/FintechCard03';
import FintechCard04 from '../partials/fintech/FintechCard04';
import FintechCard05 from '../partials/fintech/FintechCard05';
import FintechCard06 from '../partials/fintech/FintechCard06';
import FintechCard07 from '../partials/fintech/FintechCard07';
import FintechCard08 from '../partials/fintech/FintechCard08';
import FintechCard09 from '../partials/fintech/FintechCard09';
import FintechCard10 from '../partials/fintech/FintechCard10';
import FintechCard11 from '../partials/fintech/FintechCard11';
import FintechCard12 from '../partials/fintech/FintechCard12';
import FintechCard13 from '../partials/fintech/FintechCard13';
import FintechCard14 from '../partials/fintech/FintechCard14';
function Fintech() {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<div className='flex h-screen overflow-hidden'>
{/* Sidebar */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* Content area */}
<div className='relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden'>
{/* Site header */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<main>
<div className='px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto'>
{/* Page header */}
<div className='sm:flex sm:justify-between sm:items-center mb-5'>
{/* Left: Title */}
<div className='mb-4 sm:mb-0'>
<h1 className='text-2xl md:text-3xl text-slate-800 font-bold'>Fintech </h1>
</div>
{/* Right: Actions */}
<div className='grid grid-flow-col sm:auto-cols-max justify-start sm:justify-end gap-2'>
{/* Datepicker built with flatpickr */}
<Datepicker align='right' />
{/* Add account button */}
<button className='btn bg-indigo-500 hover:bg-indigo-600 text-white'>
<svg className='w-4 h-4 fill-current opacity-50 shrink-0' viewBox='0 0 16 16'>
<path d='M15 7H9V1c0-.6-.4-1-1-1S7 .4 7 1v6H1c-.6 0-1 .4-1 1s.4 1 1 1h6v6c0 .6.4 1 1 1s1-.4 1-1V9h6c.6 0 1-.4 1-1s-.4-1-1-1z' />
</svg>
<span className='hidden xs:block ml-2'>Add Account</span>
</button>
</div>
</div>
{/* Cards */}
<div className='grid grid-cols-12 gap-6'>
{/* Page Intro */}
<FintechIntro />
{/* Line chart (Portfolio Returns) */}
<FintechCard01 />
{/* Credit Card */}
<FintechCard02 />
{/* Bar chart (Cash Flow) */}
<FintechCard03 />
{/* Horizontal bar chart (Cash Flow by Account) */}
<FintechCard04 />
{/* Table (Recent Expenses) */}
<FintechCard05 />
{/* Table (Recent Earnings) */}
<FintechCard06 />
{/* Line chart (Saving Goals) */}
<FintechCard07 />
{/* Line chart (Growth Portfolio) */}
<FintechCard08 />
{/* Pie chart (Portfolio Value) */}
<FintechCard09 />
{/* Line charts (Stock graphs) */}
<FintechCard10 />
<FintechCard11 />
<FintechCard12 />
<FintechCard13 />
{/* Table (Market Trends) */}
<FintechCard14 />
</div>
</div>
</main>
</div>
</div>
);
}
export default Fintech;

View File

@@ -1,55 +0,0 @@
import React, { useState, useEffect, useRef } from 'react';
import Sidebar from '../partials/Sidebar';
import Header from '../partials/Header';
import MessagesSidebar from '../partials/messages/MessagesSidebar';
import MessagesHeader from '../partials/messages/MessagesHeader';
import MessagesBody from '../partials/messages/MessagesBody';
import MessagesFooter from '../partials/messages/MessagesFooter';
function Messages() {
const contentArea = useRef(null)
const [sidebarOpen, setSidebarOpen] = useState(false);
const [msgSidebarOpen, setMsgSidebarOpen] = useState(true);
useEffect(() => {
contentArea.current.scrollTop = 99999999
}, [msgSidebarOpen]); // automatically scroll the chat and make the most recent message visible
return (
<div className="flex h-screen overflow-hidden">
{/* Sidebar */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* Content area */}
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden" ref={contentArea}>
{/* Site header */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<main>
<div className="relative flex">
{/* Messages sidebar */}
<MessagesSidebar msgSidebarOpen={msgSidebarOpen} setMsgSidebarOpen={setMsgSidebarOpen} />
{/* Messages body */}
<div className={`grow flex flex-col md:translate-x-0 transform transition-transform duration-300 ease-in-out ${msgSidebarOpen ? 'translate-x-1/3' : 'translate-x-0'}`}>
<MessagesHeader msgSidebarOpen={msgSidebarOpen} setMsgSidebarOpen={setMsgSidebarOpen} />
<MessagesBody />
<MessagesFooter />
</div>
</div>
</main>
</div>
</div>
);
}
export default Messages;

View File

@@ -1,37 +1,41 @@
import React, {useEffect} from 'react'; import React, { useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import OnboardingImage from '../images/onboarding-image.jpg'; import OnboardingImage from '../images/onboarding-image.jpg';
import OnboardingDecoration from '../images/auth-decoration.png'; import OnboardingDecoration from '../images/auth-decoration.png';
import Logo from "../images/logo.png"; import Logo from '../images/logo.png';
import { observer } from "mobx-react-lite"; import { observer } from 'mobx-react-lite';
import { registrationStore } from "../store/store"; import { store } from '../store/store';
import { Navigate } from 'react-router-dom';
const Onboarding1 = observer(() => { const Onboarding1 = observer(() => {
useEffect(() => {
store.clearDataRegistration();
}, []);
useEffect(()=>{ if (store.passPhrase)
registrationStore.generatePassPhrase() return <Navigate to="/digitalId/profile-id" replace={true} />;
}, [])
return ( return (
<main className="bg-white"> <main className="bg-white">
<div className="relative flex"> <div className="relative flex">
{/* Content */} {/* Content */}
<div className="w-full md:w-1/2"> <div className="w-full md:w-1/2">
<div className="min-h-screen h-full flex flex-col after:flex-1"> <div className="min-h-screen h-full flex flex-col after:flex-1">
<div className="flex-1"> <div className="flex-1">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8"> <div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8">
{/* Logo */} {/* Logo */}
<Link className="block" to="/dashboard"> <Link className="block" to="/dashboard">
<img alt='logo' src={Logo} width="89" height="32"/> <img alt="logo" src={Logo} width="89" height="32" />
</Link> </Link>
<div className="text-sm"> <div className="text-sm">
Have an account? <Link className="font-medium text-indigo-500 hover:text-indigo-600" to="/signIn">Sign In</Link> Have an account?{' '}
<Link
className="font-medium text-indigo-500 hover:text-indigo-600"
to="/signIn"
>
Sign In
</Link>
</div> </div>
</div> </div>
@@ -39,19 +43,42 @@ const Onboarding1 = observer(() => {
<div className="px-4 pt-12 pb-[142px]"> <div className="px-4 pt-12 pb-[142px]">
<div className="max-w-md mx-auto w-full"> <div className="max-w-md mx-auto w-full">
<div className="relative"> <div className="relative">
<div className="absolute left-0 top-1/2 -mt-px w-full h-0.5 bg-slate-200" aria-hidden="true"></div> <div
className="absolute left-0 top-1/2 -mt-px w-full h-0.5 bg-slate-200"
aria-hidden="true"
></div>
<ul className="relative flex justify-between w-full"> <ul className="relative flex justify-between w-full">
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white" to="/">1</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white"
to="/"
>
1
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500" to="/onboarding-2">2</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500"
to="/onboarding-2"
>
2
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500" to="/onboarding-3">3</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500"
to="/onboarding-3"
>
3
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500" to="/onboarding-4">4</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500"
to="/onboarding-4"
>
4
</span>
</li> </li>
</ul> </ul>
</div> </div>
@@ -61,49 +88,120 @@ const Onboarding1 = observer(() => {
<div className="px-4 py-8"> <div className="px-4 py-8">
<div className="max-w-md mx-auto"> <div className="max-w-md mx-auto">
<h1 className="text-3xl text-slate-800 font-bold mb-12">
<h1 className="text-3xl text-slate-800 font-bold mb-12">Tell us about your company </h1> Tell us about your company
</h1>
{/* Form */} {/* Form */}
<div> <div>
<div className="sm:flex space-y-3 sm:space-y-0 sm:space-x-4 mb-8"> <div className="sm:flex space-y-3 sm:space-y-0 sm:space-x-4 mb-8">
<label className="flex-1 relative block cursor-pointer"> <label className="flex-1 relative block cursor-pointer">
<input type="radio" name="radio-buttons" className="peer sr-only" defaultChecked /> <input
type="radio"
name="radio-buttons"
className="peer sr-only"
defaultChecked
/>
<div className="h-full text-center bg-white px-4 py-6 rounded border border-slate-200 hover:border-slate-300 shadow-sm duration-150 ease-in-out"> <div className="h-full text-center bg-white px-4 py-6 rounded border border-slate-200 hover:border-slate-300 shadow-sm duration-150 ease-in-out">
<svg className="inline-flex w-10 h-10 shrink-0 fill-current mb-2" viewBox="0 0 40 40"> <svg
<circle className="text-indigo-100" cx="20" cy="20" r="20" /> className="inline-flex w-10 h-10 shrink-0 fill-current mb-2"
<path className="text-indigo-500" d="m26.371 23.749-3.742-1.5a1 1 0 0 1-.629-.926v-.878A3.982 3.982 0 0 0 24 17v-1.828A4.087 4.087 0 0 0 20 11a4.087 4.087 0 0 0-4 4.172V17a3.982 3.982 0 0 0 2 3.445v.878a1 1 0 0 1-.629.928l-3.742 1.5a1 1 0 0 0-.629.926V27a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.323a1 1 0 0 0-.629-.928Z" /> viewBox="0 0 40 40"
>
<circle
className="text-indigo-100"
cx="20"
cy="20"
r="20"
/>
<path
className="text-indigo-500"
d="m26.371 23.749-3.742-1.5a1 1 0 0 1-.629-.926v-.878A3.982 3.982 0 0 0 24 17v-1.828A4.087 4.087 0 0 0 20 11a4.087 4.087 0 0 0-4 4.172V17a3.982 3.982 0 0 0 2 3.445v.878a1 1 0 0 1-.629.928l-3.742 1.5a1 1 0 0 0-.629.926V27a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.323a1 1 0 0 0-.629-.928Z"
/>
</svg> </svg>
<div className="font-semibold text-slate-800 mb-1">Individual</div> <div className="font-semibold text-slate-800 mb-1">
<div className="text-sm">Lorem ipsum is place text commonly used.</div> Individual
</div> </div>
<div className="absolute inset-0 border-2 border-transparent peer-checked:border-indigo-400 rounded pointer-events-none" aria-hidden="true"></div> <div className="text-sm">
Lorem ipsum is place text commonly used.
</div>
</div>
<div
className="absolute inset-0 border-2 border-transparent peer-checked:border-indigo-400 rounded pointer-events-none"
aria-hidden="true"
></div>
</label> </label>
<label className="flex-1 relative block cursor-pointer"> <label className="flex-1 relative block cursor-pointer">
<input type="radio" name="radio-buttons" className="peer sr-only" /> <input
type="radio"
name="radio-buttons"
className="peer sr-only"
/>
<div className="h-full text-center bg-white px-4 py-6 rounded border border-slate-200 hover:border-slate-300 shadow-sm duration-150 ease-in-out"> <div className="h-full text-center bg-white px-4 py-6 rounded border border-slate-200 hover:border-slate-300 shadow-sm duration-150 ease-in-out">
<svg className="inline-flex w-10 h-10 shrink-0 fill-current mb-2" viewBox="0 0 40 40"> <svg
<circle className="text-indigo-100" cx="20" cy="20" r="20" /> className="inline-flex w-10 h-10 shrink-0 fill-current mb-2"
<path className="text-indigo-500" d="m26.371 23.749-3.742-1.5a1 1 0 0 1-.629-.926v-.878A3.982 3.982 0 0 0 24 17v-1.828A4.087 4.087 0 0 0 20 11a4.087 4.087 0 0 0-4 4.172V17a3.982 3.982 0 0 0 2 3.445v.878a1 1 0 0 1-.629.928l-3.742 1.5a1 1 0 0 0-.629.926V27a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.323a1 1 0 0 0-.629-.928Z" /> viewBox="0 0 40 40"
<circle className="text-indigo-100" cx="20" cy="20" r="20" /> >
<path className="text-indigo-300" d="m30.377 22.749-3.709-1.5a1 1 0 0 1-.623-.926v-.878A3.989 3.989 0 0 0 28.027 16v-1.828c.047-2.257-1.728-4.124-3.964-4.172-2.236.048-4.011 1.915-3.964 4.172V16a3.989 3.989 0 0 0 1.982 3.445v.878a1 1 0 0 1-.623.928c-.906.266-1.626.557-2.159.872-.533.315-1.3 1.272-2.299 2.872 1.131.453 6.075-.546 6.072.682V28a2.99 2.99 0 0 1-.182 1h7.119A.996.996 0 0 0 31 28v-4.323a1 1 0 0 0-.623-.928Z" /> <circle
<path className="text-indigo-500" d="m22.371 24.749-3.742-1.5a1 1 0 0 1-.629-.926v-.878A3.982 3.982 0 0 0 20 18v-1.828A4.087 4.087 0 0 0 16 12a4.087 4.087 0 0 0-4 4.172V18a3.982 3.982 0 0 0 2 3.445v.878a1 1 0 0 1-.629.928l-3.742 1.5a1 1 0 0 0-.629.926V28a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.323a1 1 0 0 0-.629-.928Z" /> className="text-indigo-100"
cx="20"
cy="20"
r="20"
/>
<path
className="text-indigo-500"
d="m26.371 23.749-3.742-1.5a1 1 0 0 1-.629-.926v-.878A3.982 3.982 0 0 0 24 17v-1.828A4.087 4.087 0 0 0 20 11a4.087 4.087 0 0 0-4 4.172V17a3.982 3.982 0 0 0 2 3.445v.878a1 1 0 0 1-.629.928l-3.742 1.5a1 1 0 0 0-.629.926V27a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.323a1 1 0 0 0-.629-.928Z"
/>
<circle
className="text-indigo-100"
cx="20"
cy="20"
r="20"
/>
<path
className="text-indigo-300"
d="m30.377 22.749-3.709-1.5a1 1 0 0 1-.623-.926v-.878A3.989 3.989 0 0 0 28.027 16v-1.828c.047-2.257-1.728-4.124-3.964-4.172-2.236.048-4.011 1.915-3.964 4.172V16a3.989 3.989 0 0 0 1.982 3.445v.878a1 1 0 0 1-.623.928c-.906.266-1.626.557-2.159.872-.533.315-1.3 1.272-2.299 2.872 1.131.453 6.075-.546 6.072.682V28a2.99 2.99 0 0 1-.182 1h7.119A.996.996 0 0 0 31 28v-4.323a1 1 0 0 0-.623-.928Z"
/>
<path
className="text-indigo-500"
d="m22.371 24.749-3.742-1.5a1 1 0 0 1-.629-.926v-.878A3.982 3.982 0 0 0 20 18v-1.828A4.087 4.087 0 0 0 16 12a4.087 4.087 0 0 0-4 4.172V18a3.982 3.982 0 0 0 2 3.445v.878a1 1 0 0 1-.629.928l-3.742 1.5a1 1 0 0 0-.629.926V28a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.323a1 1 0 0 0-.629-.928Z"
/>
</svg> </svg>
<div className="font-semibold text-slate-800 mb-1">Organization</div> <div className="font-semibold text-slate-800 mb-1">
<div className="text-sm">Lorem ipsum is place text commonly used.</div> Organization
</div> </div>
<div className="absolute inset-0 border-2 border-transparent peer-checked:border-indigo-400 rounded pointer-events-none" aria-hidden="true"></div> <div className="text-sm">
Lorem ipsum is place text commonly used.
</div>
</div>
<div
className="absolute inset-0 border-2 border-transparent peer-checked:border-indigo-400 rounded pointer-events-none"
aria-hidden="true"
></div>
</label> </label>
</div> </div>
<div className="flex items-center justify-between space-x-6 mb-8"> <div className="flex items-center justify-between space-x-6 mb-8">
<div> <div>
<div className="font-medium text-slate-800 text-sm mb-1">💸 Lorem ipsum is place text commonly?</div> <div className="font-medium text-slate-800 text-sm mb-1">
<div className="text-xs font-normal leading-6">Lorem ipsum is placeholder text commonly used in the graphic, print, and publishing industries for previewing layouts.</div> 💸 Lorem ipsum is place text commonly?
</div>
<div className="text-xs font-normal leading-6">
Lorem ipsum is placeholder text commonly used in the
graphic, print, and publishing industries for previewing
layouts.
</div>
</div> </div>
<div className="flex items-center"> <div className="flex items-center">
<div className="form-switch"> <div className="form-switch">
<input type="checkbox" id="switch" className="sr-only" defaultChecked /> <input
type="checkbox"
id="switch"
className="sr-only"
defaultChecked
/>
<label className="bg-slate-400" htmlFor="switch"> <label className="bg-slate-400" htmlFor="switch">
<span className="bg-white shadow-sm" aria-hidden="true"></span> <span
className="bg-white shadow-sm"
aria-hidden="true"
></span>
<span className="sr-only">Switch label</span> <span className="sr-only">Switch label</span>
</label> </label>
</div> </div>
@@ -115,24 +213,34 @@ const Onboarding1 = observer(() => {
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Image */} {/* Image */}
<div className="hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2" aria-hidden="true"> <div
<img className="object-cover object-center w-full h-full" src={OnboardingImage} width="760" height="1024" alt="Onboarding" /> className="hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2"
<img className="absolute top-1/4 left-0 transform -translate-x-1/2 ml-8 hidden lg:block" src={OnboardingDecoration} width="218" height="224" alt="Authentication decoration" /> aria-hidden="true"
>
<img
className="object-cover object-center w-full h-full"
src={OnboardingImage}
width="760"
height="1024"
alt="Onboarding"
/>
<img
className="absolute top-1/4 left-0 transform -translate-x-1/2 ml-8 hidden lg:block"
src={OnboardingDecoration}
width="218"
height="224"
alt="Authentication decoration"
/>
</div> </div>
</div> </div>
</main> </main>
); );
}) });
export default Onboarding1; export default Onboarding1;

View File

@@ -1,67 +1,81 @@
import React, {useState} from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { observer } from "mobx-react-lite"; import { observer } from 'mobx-react-lite';
import { registrationStore } from "../store/store"; import { store } from '../store/store';
import OnboardingImage from '../images/onboarding-image.jpg'; import OnboardingImage from '../images/onboarding-image.jpg';
import OnboardingDecoration from '../images/auth-decoration.png'; import OnboardingDecoration from '../images/auth-decoration.png';
import Logo from "../images/logo.png"; import Logo from '../images/logo.png';
const Onboarding2 = observer(() => { const Onboarding2 = observer(() => {
const [dataRegistration, setDataRegistration] = useState( const navigate = useNavigate();
{
const [dataRegistration, setDataRegistration] = useState({
firstname: '', firstname: '',
secondname: '', secondname: '',
gender: '', gender: '',
birthdate: '', birthdate: '',
} });
const fillingForm = useMemo(
() =>
!Object.keys(dataRegistration).find((item) => !dataRegistration[item]),
[dataRegistration]
); );
const [fillingForm, setFillingForm] = useState(false);
const saveValueChange = (event) => { const saveValueChange = (event) => {
const newValue=event.target.value; const newValue = event.target.value;
setDataRegistration({ setDataRegistration({
...dataRegistration, ...dataRegistration,
[event.target.id]: newValue, [event.target.id]: newValue,
}) });
};
const checkFillingForm = () => {
if (!Object.keys(dataRegistration).find(item => !dataRegistration[item])) {
setFillingForm(true);
}
}; };
const saveStoreRegistration = () => { const saveStoreRegistration = () => {
if (fillingForm) { if (fillingForm) {
registrationStore.saveDataRegistration(Object.keys(dataRegistration).map(item => { store.saveDataRegistration(
return{ Object.keys(dataRegistration).map((item) => ({
key: `${item}`, key: `${item}`,
value: dataRegistration[item], value: dataRegistration[item],
}
})) }))
);
navigate('/onboarding-3', { replace: true });
} }
}; };
useEffect(() => {
if (store.accountData.length === 0) return;
setDataRegistration(
store.accountData.reduce(
(acc, item) => ({
...acc,
[item.key]: item.value,
}),
{}
)
);
}, []);
return ( return (
<main className="bg-white"> <main className="bg-white">
<div className="relative flex"> <div className="relative flex">
{/* Content */} {/* Content */}
<div className="w-full md:w-1/2"> <div className="w-full md:w-1/2">
<div className="min-h-screen h-full flex flex-col after:flex-1"> <div className="min-h-screen h-full flex flex-col after:flex-1">
<div className="flex-1"> <div className="flex-1">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8"> <div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8">
{/* Logo */} {/* Logo */}
<Link className="block" to="/dashboard"> <Link className="block" to="/dashboard">
<img alt='logo' src={Logo} width="89" height="32"/> <img alt="logo" src={Logo} width="89" height="32" />
</Link> </Link>
<div className="text-sm"> <div className="text-sm">
Have an account? <Link className="font-medium text-indigo-500 hover:text-indigo-600" to="/signIn">Sign In</Link> Have an account?{' '}
<Link
className="font-medium text-indigo-500 hover:text-indigo-600"
to="/signIn"
>
Sign In
</Link>
</div> </div>
</div> </div>
@@ -69,19 +83,42 @@ const Onboarding2 = observer(() => {
<div className="px-4 pt-12 pb-[59px]"> <div className="px-4 pt-12 pb-[59px]">
<div className="max-w-md mx-auto w-full"> <div className="max-w-md mx-auto w-full">
<div className="relative"> <div className="relative">
<div className="absolute left-0 top-1/2 -mt-px w-full h-0.5 bg-slate-200" aria-hidden="true"></div> <div
className="absolute left-0 top-1/2 -mt-px w-full h-0.5 bg-slate-200"
aria-hidden="true"
></div>
<ul className="relative flex justify-between w-full"> <ul className="relative flex justify-between w-full">
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white" to="/">1</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white"
to="/"
>
1
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white" to="/onboarding-2">2</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white"
to="/onboarding-2"
>
2
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500" to="/onboarding-3">3</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500"
to="/onboarding-3"
>
3
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500" to="/onboarding-4">4</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500"
to="/onboarding-4"
>
4
</span>
</li> </li>
</ul> </ul>
</div> </div>
@@ -91,64 +128,120 @@ const Onboarding2 = observer(() => {
<div className="px-4 py-8"> <div className="px-4 py-8">
<div className="max-w-[344px] mx-auto"> <div className="max-w-[344px] mx-auto">
<h1 className="text-3xl text-slate-800 font-bold mb-6">Create your Account </h1> <h1 className="text-3xl text-slate-800 font-bold mb-6">
Create your Account
</h1>
{/* Form */} {/* Form */}
<div> <div>
<div className="space-y-4 mb-8"> <div className="space-y-4 mb-8">
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="first_name">First name <span className="text-rose-500">*</span></label> <label
<input onBlur={checkFillingForm} id="firstname" onChange={(event)=>saveValueChange(event)} className="form-input w-full" type="text" /> className="block text-sm font-medium mb-1"
htmlFor="first_name"
>
First name <span className="text-rose-500">*</span>
</label>
<input
id="firstname"
onChange={(event) => saveValueChange(event)}
className="form-input w-full"
type="text"
value={dataRegistration.firstname}
/>
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="second_name">Last name <span className="text-rose-500">*</span></label> <label
<input onBlur={checkFillingForm} id="secondname" onChange={(event)=>saveValueChange(event)} className="form-input w-full" type="text" /> className="block text-sm font-medium mb-1"
htmlFor="second_name"
>
Last name <span className="text-rose-500">*</span>
</label>
<input
id="secondname"
onChange={(event) => saveValueChange(event)}
className="form-input w-full"
type="text"
value={dataRegistration.secondname}
/>
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="gender">Gender <span className="text-rose-500">*</span></label> <label
<select onBlur={checkFillingForm} id="gender" onChange={(event)=>saveValueChange(event)} className="form-select w-full"> className="block text-sm font-medium mb-1"
htmlFor="gender"
>
Gender <span className="text-rose-500">*</span>
</label>
<select
id="gender"
onChange={(event) => saveValueChange(event)}
className="form-select w-full"
value={dataRegistration.gender}
>
<option className="hidden"></option> <option className="hidden"></option>
<option>male</option> <option>male</option>
<option>female</option> <option>female</option>
</select> </select>
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="birthdate">Date of birth <span className="text-rose-500">*</span></label> <label
<input onBlur={checkFillingForm} id="birthdate" onChange={(event)=>saveValueChange(event)} className="form-input w-full" type="date" autoComplete="on" /> className="block text-sm font-medium mb-1"
htmlFor="birthdate"
>
Date of birth <span className="text-rose-500">*</span>
</label>
<input
id="birthdate"
onChange={(event) => saveValueChange(event)}
className="form-input w-full"
type="date"
autoComplete="on"
value={dataRegistration.birthdate}
/>
</div> </div>
</div> </div>
<div className="flex items-center justify-between mt-6"> <div className="flex items-center justify-between mt-6">
<div className="mr-1"> <div className="mr-1">
<label className="flex items-center"> <label className="flex items-center">
<input type="checkbox" className="form-checkbox" /> <input type="checkbox" className="form-checkbox" />
<span className="text-sm ml-2">Email me about product news.</span> <span className="text-sm ml-2">
Email me about product news.
</span>
</label> </label>
</div> </div>
<button onClick={saveStoreRegistration} className="btn bg-indigo-500 hover:bg-indigo-600 text-white ml-3 whitespace-nowrap"> <button
<Link id="signup" to={fillingForm && "/onboarding-3"} >Sign Up</Link> onClick={saveStoreRegistration}
className="btn bg-indigo-500 hover:bg-indigo-600 text-white ml-3 whitespace-nowrap"
>
Sign Up
</button> </button>
</div> </div>
</div> </div>
{/* Footer */}
<div className="pt-5 mt-6 border-t border-slate-200">
<div className="text-sm">
Have an account? <Link className="font-medium text-indigo-500 hover:text-indigo-600" to="/signIn">Sign In</Link>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
{/* Image */} {/* Image */}
<div className="hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2" aria-hidden="true"> <div
<img className="object-cover object-center w-full h-full" src={OnboardingImage} width="760" height="1024" alt="Onboarding" /> className="hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2"
<img className="absolute top-1/4 left-0 transform -translate-x-1/2 ml-8 hidden lg:block" src={OnboardingDecoration} width="218" height="224" alt="Authentication decoration" /> aria-hidden="true"
>
<img
className="object-cover object-center w-full h-full"
src={OnboardingImage}
width="760"
height="1024"
alt="Onboarding"
/>
<img
className="absolute top-1/4 left-0 transform -translate-x-1/2 ml-8 hidden lg:block"
src={OnboardingDecoration}
width="218"
height="224"
alt="Authentication decoration"
/>
</div> </div>
</div> </div>
</main> </main>
); );
}); });

View File

@@ -1,49 +1,55 @@
import React from 'react'; import React, { useEffect } from 'react';
import {observer} from "mobx-react-lite"; import { observer } from 'mobx-react-lite';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg"; import { generateSvgAvatar } from '../images/GenerateOnboardingSvg/GenerateSvg';
import Logo from "../images/logo.png"; import Logo from '../images/logo.png';
import {registrationStore} from "../store/store"; import { store } from '../store/store';
const Onboarding3 = observer(()=>{ const Onboarding3 = observer(() => {
function generatePassPhrase() { useEffect(() => {
registrationStore.generatePassPhrase(); store.generatePassPhrase();
}, []);
const generatePassPhrase = () => {
store.generatePassPhrase();
}; };
function savePassPhraseInStore (copiedPhrase) { const savePassPhraseInStore = (copiedPhrase) => {
if (copiedPhrase.split(' ').length === 12) { if (copiedPhrase.split(' ').length === 12) {
registrationStore.savePastPassPhrase(copiedPhrase.trim()); store.savePastPassPhrase(copiedPhrase.trim());
} }
}; };
function pastePassPhrase() { const pastePassPhrase = () => {
navigator.clipboard.readText().then(res => savePassPhraseInStore(res)); navigator.clipboard.readText().then((res) => savePassPhraseInStore(res));
}; };
function convertPassPhraseToArray() { const convertPassPhraseToArray = () =>
return registrationStore.passPhrase.split(' ').map((item, index) => ({str:item, id:index})); store.passPhrase
}; .split(' ')
.map((item, index) => ({ str: item, id: index }));
return ( return (
<main className="bg-white"> <main className="bg-white">
<div className="relative flex"> <div className="relative flex">
{/* Content */} {/* Content */}
<div className="w-full md:w-1/2"> <div className="w-full md:w-1/2">
<div className="min-h-screen h-full flex flex-col after:flex-1"> <div className="min-h-screen h-full flex flex-col after:flex-1">
<div className="flex-1"> <div className="flex-1">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8"> <div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8">
{/* Logo */} {/* Logo */}
<Link className="block" to="/dashboard"> <Link className="block" to="/dashboard">
<img alt='logo' src={Logo} width="89" height="32"/> <img alt="logo" src={Logo} width="89" height="32" />
</Link> </Link>
<div className="text-sm"> <div className="text-sm">
Have an account? <Link className="font-medium text-indigo-500 hover:text-indigo-600" to="/signIn">Sign In</Link> Have an account?{' '}
<Link
className="font-medium text-indigo-500 hover:text-indigo-600"
to="/signIn"
>
Sign In
</Link>
</div> </div>
</div> </div>
@@ -51,19 +57,42 @@ const Onboarding3 = observer(()=>{
<div className="px-4 pt-12 pb-8"> <div className="px-4 pt-12 pb-8">
<div className="max-w-md mx-auto w-full"> <div className="max-w-md mx-auto w-full">
<div className="relative"> <div className="relative">
<div className="absolute left-0 top-1/2 -mt-px w-full h-0.5 bg-slate-200" aria-hidden="true"></div> <div
className="absolute left-0 top-1/2 -mt-px w-full h-0.5 bg-slate-200"
aria-hidden="true"
></div>
<ul className="relative flex justify-between w-full"> <ul className="relative flex justify-between w-full">
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white" to="/">1</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white"
to="/"
>
1
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white" to="/onboarding-2">2</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white"
to="/onboarding-2"
>
2
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white" to="/onboarding-3">3</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white"
to="/onboarding-3"
>
3
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500" to="/onboarding-4">4</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-slate-100 text-slate-500"
to="/onboarding-4"
>
4
</span>
</li> </li>
</ul> </ul>
</div> </div>
@@ -73,58 +102,94 @@ const Onboarding3 = observer(()=>{
<div className="px-4 mt-20 py-8"> <div className="px-4 mt-20 py-8">
<div className="max-w-fit mx-auto flex flex-col gap-y-36"> <div className="max-w-fit mx-auto flex flex-col gap-y-36">
<div className="gap-0.5"> <div className="gap-0.5">
<h1 className="text-3xl text-slate-800 font-bold mb-12">Company information </h1> <h1 className="text-3xl text-slate-800 font-bold mb-12">
Company information
</h1>
{/* htmlForm */} {/* htmlForm */}
<div className="grid grid-cols-4 gap-y-2.5 gap-[18px] mb-14"> <div className="grid grid-cols-4 gap-y-2.5 gap-[18px] mb-14">
{convertPassPhraseToArray().map(tag => ( {convertPassPhraseToArray().map((tag) => (
<button key={tag.id} className="min-w-93px w-24 min-h-26px h-26px bg-slate-100 text-slate-500 cursor-pointer hover:bg-blue-100 hover:text-blue-600 rounded-full text-center px-2.5"> <button
key={tag.id}
className="min-w-93px w-24 min-h-26px h-26px bg-slate-100 text-slate-500 cursor-pointer hover:bg-blue-100 hover:text-blue-600 rounded-full text-center px-2.5"
>
<span className="text-sm font-medium">{tag.str}</span> <span className="text-sm font-medium">{tag.str}</span>
</button> </button>
))} ))}
</div> </div>
<div className="flex pl-7 gap-x-5"> <div className="flex pl-7 gap-x-5">
<button onClick={pastePassPhrase} className="btn pl-9 pr-16 border-slate-200 hover:border-slate-300 text-slate-600"> <button
<svg className="w-4 h-4 fill-current text-slate-400 shrink-0 mb-0.5" viewBox="0 0 16 16"> onClick={pastePassPhrase}
<path className="btn pl-9 pr-16 border-slate-200 hover:border-slate-300 text-slate-600"
d="M11.7.3c-.4-.4-1-.4-1.4 0l-10 10c-.2.2-.3.4-.3.7v4c0 .6.4 1 1 1h4c.3 0 .5-.1.7-.3l10-10c.4-.4.4-1 0-1.4l-4-4zM4.6 14H2v-2.6l6-6L10.6 8l-6 6zM12 6.6L9.4 4 11 2.4 13.6 5 12 6.6z"></path> >
<svg
className="w-4 h-4 fill-current text-slate-400 shrink-0 mb-0.5"
viewBox="0 0 16 16"
>
<path d="M11.7.3c-.4-.4-1-.4-1.4 0l-10 10c-.2.2-.3.4-.3.7v4c0 .6.4 1 1 1h4c.3 0 .5-.1.7-.3l10-10c.4-.4.4-1 0-1.4l-4-4zM4.6 14H2v-2.6l6-6L10.6 8l-6 6zM12 6.6L9.4 4 11 2.4 13.6 5 12 6.6z"></path>
</svg> </svg>
<span className="ml-2">Paste</span> <span className="ml-2">Paste</span>
</button> </button>
<button onClick={generatePassPhrase} className="btn pr-11 pl-7 border-slate-200 hover:border-slate-300 text-rose-500"> <button
<svg className="mb-0.5" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> onClick={generatePassPhrase}
<path d="M14.5744 5.66943L13.1504 7.09343C13.4284 7.44043 13.6564 7.75743 13.8194 8.00043C13.0594 9.13043 10.9694 11.8204 8.25836 11.9854L6.44336 13.8004C6.93936 13.9244 7.45736 14.0004 8.00036 14.0004C12.7074 14.0004 15.7444 8.71643 15.8714 8.49243C16.0424 8.18843 16.0434 7.81643 15.8724 7.51243C15.8254 7.42743 15.3724 6.63143 14.5744 5.66943Z" fill="#F43F5E"/> className="btn pr-11 pl-7 border-slate-200 hover:border-slate-300 text-rose-500"
<path fillRule="evenodd" clipRule="evenodd" d="M1.00038 16.0002C0.744375 16.0002 0.488375 15.9022 0.293375 15.7072C-0.0976249 15.3162 -0.0976249 14.6842 0.292375 14.2942L2.82138 11.7653C1.17238 10.2913 0.198375 8.61425 0.128375 8.48925C-0.0416249 8.18625 -0.0426249 7.81725 0.126375 7.51425C0.251375 7.28925 3.24537 2.00025 8.00037 2.00025C9.33138 2.00025 10.5154 2.43125 11.5484 3.03825L14.2934 0.29325C14.6844 -0.09775 15.3164 -0.09775 15.7074 0.29325C16.0984 0.68425 16.0984 1.31625 15.7074 1.70725L1.70738 15.7072C1.51238 15.9022 1.25638 16.0002 1.00038 16.0002ZM8.00037 4.00025C5.14637 4.00025 2.95837 6.83525 2.18138 7.99925C2.55938 8.56225 3.28538 9.51025 4.24038 10.3463L6.07438 8.51225C6.02938 8.34825 6.00037 8.17825 6.00037 8.00025C6.00037 6.89525 6.89537 6.00025 8.00037 6.00025C8.17838 6.00025 8.34838 6.02925 8.51237 6.07425L10.0784 4.50825C9.43738 4.20125 8.74237 4.00025 8.00037 4.00025Z" fill="#F43F5E"/> >
<svg
className="mb-0.5"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.5744 5.66943L13.1504 7.09343C13.4284 7.44043 13.6564 7.75743 13.8194 8.00043C13.0594 9.13043 10.9694 11.8204 8.25836 11.9854L6.44336 13.8004C6.93936 13.9244 7.45736 14.0004 8.00036 14.0004C12.7074 14.0004 15.7444 8.71643 15.8714 8.49243C16.0424 8.18843 16.0434 7.81643 15.8724 7.51243C15.8254 7.42743 15.3724 6.63143 14.5744 5.66943Z"
fill="#F43F5E"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1.00038 16.0002C0.744375 16.0002 0.488375 15.9022 0.293375 15.7072C-0.0976249 15.3162 -0.0976249 14.6842 0.292375 14.2942L2.82138 11.7653C1.17238 10.2913 0.198375 8.61425 0.128375 8.48925C-0.0416249 8.18625 -0.0426249 7.81725 0.126375 7.51425C0.251375 7.28925 3.24537 2.00025 8.00037 2.00025C9.33138 2.00025 10.5154 2.43125 11.5484 3.03825L14.2934 0.29325C14.6844 -0.09775 15.3164 -0.09775 15.7074 0.29325C16.0984 0.68425 16.0984 1.31625 15.7074 1.70725L1.70738 15.7072C1.51238 15.9022 1.25638 16.0002 1.00038 16.0002ZM8.00037 4.00025C5.14637 4.00025 2.95837 6.83525 2.18138 7.99925C2.55938 8.56225 3.28538 9.51025 4.24038 10.3463L6.07438 8.51225C6.02938 8.34825 6.00037 8.17825 6.00037 8.00025C6.00037 6.89525 6.89537 6.00025 8.00037 6.00025C8.17838 6.00025 8.34838 6.02925 8.51237 6.07425L10.0784 4.50825C9.43738 4.20125 8.74237 4.00025 8.00037 4.00025Z"
fill="#F43F5E"
/>
</svg> </svg>
<span className="ml-2">Generate</span> <span className="ml-2">Generate</span>
</button> </button>
</div> </div>
</div> </div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Link className="text-sm underline hover:no-underline" to="/onboarding-2">&lt;- Back</Link> <Link
className="text-sm underline hover:no-underline"
to="/onboarding-2"
>
&lt;- Back
</Link>
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white ml-auto"> <button className="btn bg-indigo-500 hover:bg-indigo-600 text-white ml-auto">
<Link to="/onboarding-4">Next Step -&gt;</Link> <Link to="/onboarding-4">Next Step -&gt;</Link>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Image */} {/* Image */}
<div className="flex flex-col items-center h-full w-full hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2" aria-hidden="true"> <div
className="flex flex-col items-center h-full w-full hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2"
aria-hidden="true"
>
<div className="flex mt-40 flex-col items-center gap-2.5"> <div className="flex mt-40 flex-col items-center gap-2.5">
<img className="object-cover object-center" src={generateSvgAvatar(registrationStore.pubKey)} width="493px" height="493px" alt="Onboarding" /> <img
className="object-cover object-center"
src={generateSvgAvatar(store.pubKey)}
width="493px"
height="493px"
alt="Onboarding"
/>
<span className="text-sm">Your generated Digital ID</span> <span className="text-sm">Your generated Digital ID</span>
</div> </div>
</div> </div>
</div> </div>
</main> </main>
); );
}); });

View File

@@ -1,51 +1,54 @@
import React, {useState} from 'react'; import React, { useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg"; import { generateSvgAvatar } from '../images/GenerateOnboardingSvg/GenerateSvg';
import Logo from "../images/logo.png"; import Logo from '../images/logo.png';
import {observer} from "mobx-react-lite"; import { observer } from 'mobx-react-lite';
import { registrationStore } from "../store/store"; import { store } from '../store/store';
const Onboarding4 = observer(() => { const Onboarding4 = observer(() => {
const [checkBoxesSelected, setCheckBoxesSelected] = useState([]); const [checkBoxesSelected, setCheckBoxesSelected] = useState([]);
function createAccount() { function createAccount() {
if(checkBoxesSelected.length === 2) { if (checkBoxesSelected.length === 2) {
registrationStore.pushAccountData(); store.pushAccountData();
sessionStorage.setItem('passPhrase', registrationStore.passPhrase); sessionStorage.setItem('passPhrase', store.passPhrase);
store.clearDataRegistration();
}
} }
};
function saveCheckBoxesValues(e) { function saveCheckBoxesValues(e) {
const { id, checked } = e.target; const { id, checked } = e.target;
setCheckBoxesSelected([...checkBoxesSelected, id]); setCheckBoxesSelected([...checkBoxesSelected, id]);
if (!checked) { if (!checked) {
setCheckBoxesSelected(checkBoxesSelected.filter(item => item !== id)); setCheckBoxesSelected(checkBoxesSelected.filter((item) => item !== id));
}
} }
};
const firstNameAccount = registrationStore.accountData.length && registrationStore.accountData.find(item => item.key === "firstname").value; const firstNameAccount =
store.accountData.length &&
store.accountData.find((item) => item.key === 'firstname').value;
return ( return (
<main className="bg-white"> <main className="bg-white">
<div className="relative flex"> <div className="relative flex">
{/* Content */} {/* Content */}
<div className="w-full md:w-1/2"> <div className="w-full md:w-1/2">
<div className="min-h-screen h-full flex flex-col after:flex-1"> <div className="min-h-screen h-full flex flex-col after:flex-1">
<div className="flex-1"> <div className="flex-1">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8"> <div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8">
{/* Logo */} {/* Logo */}
<Link className="block" to="/dashboard"> <Link className="block" to="/dashboard">
<img alt='logo' src={Logo} width="89" height="32"/> <img alt="logo" src={Logo} width="89" height="32" />
</Link> </Link>
<div className="text-sm"> <div className="text-sm">
Have an account? <Link className="font-medium text-indigo-500 hover:text-indigo-600" to="/signIn">Sign In</Link> Have an account?{' '}
<Link
className="font-medium text-indigo-500 hover:text-indigo-600"
to="/signIn"
>
Sign In
</Link>
</div> </div>
</div> </div>
@@ -53,19 +56,42 @@ const Onboarding4 = observer(() => {
<div className="px-4 pt-12 pb-[85px]"> <div className="px-4 pt-12 pb-[85px]">
<div className="max-w-md mx-auto w-full"> <div className="max-w-md mx-auto w-full">
<div className="relative"> <div className="relative">
<div className="absolute left-0 top-1/2 -mt-px w-full h-0.5 bg-slate-200" aria-hidden="true"></div> <div
className="absolute left-0 top-1/2 -mt-px w-full h-0.5 bg-slate-200"
aria-hidden="true"
></div>
<ul className="relative flex justify-between w-full"> <ul className="relative flex justify-between w-full">
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white" to="/">1</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white"
to="/"
>
1
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white" to="/onboarding-2">2</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white"
to="/onboarding-2"
>
2
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white" to="/onboarding-3">3</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white"
to="/onboarding-3"
>
3
</span>
</li> </li>
<li> <li>
<span className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white" to="/onboarding-4">4</span> <span
className="flex items-center justify-center w-6 h-6 rounded-full text-xs font-semibold bg-indigo-500 text-white"
to="/onboarding-4"
>
4
</span>
</li> </li>
</ul> </ul>
</div> </div>
@@ -75,50 +101,98 @@ const Onboarding4 = observer(() => {
<div className="px-4 mt-40 py-8"> <div className="px-4 mt-40 py-8">
<div className=" flex flex-col items-center gap-20 max-w-md mx-auto"> <div className=" flex flex-col items-center gap-20 max-w-md mx-auto">
<div className="text-center"> <div className="text-center">
<svg className="inline-flex w-16 h-16 fill-current mb-6" viewBox="0 0 64 64"> <svg
<circle className="text-emerald-100" cx="32" cy="32" r="32" /> className="inline-flex w-16 h-16 fill-current mb-6"
<path className="text-emerald-500" d="m28.5 41-8-8 3-3 5 5 12-12 3 3z" /> viewBox="0 0 64 64"
>
<circle
className="text-emerald-100"
cx="32"
cy="32"
r="32"
/>
<path
className="text-emerald-500"
d="m28.5 41-8-8 3-3 5 5 12-12 3 3z"
/>
</svg> </svg>
<h1 className="text-3xl text-slate-800 font-bold mb-8">{firstNameAccount ? `Nice to meet you, ${firstNameAccount} 🙌` : 'Please, go back step 2'}</h1> <h1 className="text-3xl text-slate-800 font-bold mb-8">
<button onClick={createAccount} className="btn px-6 bg-indigo-500 hover:bg-indigo-600 text-white"> {firstNameAccount
<Link id="link-dashboard" to={checkBoxesSelected.length===2 && "/dashboard"}>Go To Profile -&gt;</Link> ? `Nice to meet you, ${firstNameAccount} 🙌`
: 'Please, go back step 2'}
</h1>
<button
onClick={createAccount}
className="btn px-6 bg-indigo-500 hover:bg-indigo-600 text-white"
>
<Link
id="link-dashboard"
to={
checkBoxesSelected.length === 2 &&
'/digitalId/profile-id'
}
>
Go To Profile -&gt;
</Link>
</button> </button>
</div> </div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="mr-1"> <div className="mr-1">
<label className="flex items-center"> <label className="flex items-center">
<input id="checkbox-1" onChange={(e)=>saveCheckBoxesValues(e)} type="checkbox" className="form-checkbox" /> <input
<span className="text-sm ml-2">I understand that Idntty cannot recover pass phrase</span> id="checkbox-1"
onChange={(e) => saveCheckBoxesValues(e)}
type="checkbox"
className="form-checkbox"
/>
<span className="text-sm ml-2">
I understand that Idntty cannot recover pass phrase
</span>
</label> </label>
<label className="flex items-center"> <label className="flex items-center">
<input id="checkbox-2" onChange={(e)=>saveCheckBoxesValues(e)} type="checkbox" className="form-checkbox" /> <input
id="checkbox-2"
onChange={(e) => saveCheckBoxesValues(e)}
type="checkbox"
className="form-checkbox"
/>
<div className="text-sm ml-2"> <div className="text-sm ml-2">
I <Link className="text-indigo-500 hover:text-indigo-600 underline" to="/">store my pharse</Link> very carefuly I{' '}
<Link
className="text-indigo-500 hover:text-indigo-600 underline"
to="/"
>
store my pharse
</Link>{' '}
very carefuly
</div> </div>
</label> </label>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Image */} {/* Image */}
<div className="flex flex-col items-center h-full w-full hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2" aria-hidden="true"> <div
className="flex flex-col items-center h-full w-full hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2"
aria-hidden="true"
>
<div className="flex mt-40 flex-col items-center gap-2.5"> <div className="flex mt-40 flex-col items-center gap-2.5">
<img className="object-cover object-center" src={generateSvgAvatar(registrationStore.pubKey)} width="493px" height="493px" alt="Onboarding" /> <img
className="object-cover object-center"
src={generateSvgAvatar(store.pubKey)}
width="493px"
height="493px"
alt="Onboarding"
/>
<span className="text-sm">Your generated Digital ID</span> <span className="text-sm">Your generated Digital ID</span>
</div> </div>
</div> </div>
</div> </div>
</main> </main>
); );
}); });

View File

@@ -2,55 +2,77 @@ import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import AuthImage from '../images/auth-image.jpg'; import AuthImage from '../images/auth-image.jpg';
import AuthDecoration from '../images/auth-decoration.png'; import AuthDecoration from '../images/auth-decoration.png';
import Logo from "../images/logo.png"; import Logo from '../images/logo.png';
function ResetPassword() { function ResetPassword() {
return ( return (
<main className="bg-white"> <main className="bg-white">
<div className="relative md:flex"> <div className="relative md:flex">
{/* Content */} {/* Content */}
<div className="md:w-1/2"> <div className="md:w-1/2">
<div className="min-h-screen h-full flex flex-col after:flex-1"> <div className="min-h-screen h-full flex flex-col after:flex-1">
{/* Header */} {/* Header */}
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8"> <div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8">
{/* Logo */} {/* Logo */}
<Link className="block" to="/dashboard"> <Link className="block" to="/dashboard">
<img alt='logo' src={Logo} width="89" height="32"/> <img alt="logo" src={Logo} width="89" height="32" />
</Link> </Link>
</div> </div>
</div> </div>
<div className="max-w-sm mx-auto px-4 py-8"> <div className="max-w-sm mx-auto px-4 py-8">
<h1 className="text-3xl text-slate-800 font-bold mb-6">Reset your Password </h1> <h1 className="text-3xl text-slate-800 font-bold mb-6">
Reset your Password
</h1>
{/* Form */} {/* Form */}
<div> <div>
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="email">Email Address <span className="text-rose-500">*</span></label> <label
<input id="email" className="form-input w-full" type="email" /> className="block text-sm font-medium mb-1"
htmlFor="email"
>
Email Address <span className="text-rose-500">*</span>
</label>
<input
id="email"
className="form-input w-full"
type="email"
/>
</div> </div>
</div> </div>
<div className="flex justify-end mt-6"> <div className="flex justify-end mt-6">
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white whitespace-nowrap">Send Reset Link</button> <button className="btn bg-indigo-500 hover:bg-indigo-600 text-white whitespace-nowrap">
Send Reset Link
</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Image */} {/* Image */}
<div className="hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2" aria-hidden="true"> <div
<img className="object-cover object-center w-full h-full" src={AuthImage} width="760" height="1024" alt="Authentication" /> className="hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2"
<img className="absolute top-1/4 left-0 transform -translate-x-1/2 ml-8 hidden lg:block" src={AuthDecoration} width="218" height="224" alt="Authentication decoration" /> aria-hidden="true"
>
<img
className="object-cover object-center w-full h-full"
src={AuthImage}
width="760"
height="1024"
alt="Authentication"
/>
<img
className="absolute top-1/4 left-0 transform -translate-x-1/2 ml-8 hidden lg:block"
src={AuthDecoration}
width="218"
height="224"
alt="Authentication decoration"
/>
</div> </div>
</div> </div>
</main> </main>
); );
} }

View File

@@ -1,100 +1,153 @@
import React from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link, Navigate, useParams } from 'react-router-dom';
import {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg"; import { generateSvgAvatar } from '../images/GenerateOnboardingSvg/GenerateSvg';
import User06 from "../images/user-28-06.jpg"; import User06 from '../images/user-28-06.jpg';
import User08 from "../images/user-28-08.jpg"; import User08 from '../images/user-28-08.jpg';
import User09 from "../images/user-28-09.jpg"; import User09 from '../images/user-28-09.jpg';
import SharedDataRoadMap from "../partials/sharedData/SharedDataRoadmap"; import SharedDataRoadMap from '../partials/sharedData/SharedDataRoadmap';
import { store } from '../store/store';
import { fetchWrapper } from '../shared/fetchWrapper';
import { cryptography } from '@liskhq/lisk-client';
import { generateTransaction } from '../utils/Utils';
function SharedData() { function SharedData() {
const [encryptedData, setEncryptedData] = useState([]);
const [hasError, setHasError] = useState(false);
const sharedDataItems = { const { id } = useParams();
data: localStorage.getItem('digitalId'),
items: [ const [address, pubKey] = id.split(':');
{
dataShared: localStorage.getItem('digitalId'), const passPhrase = sessionStorage.getItem('passPhrase');
id: '923',
text: 'United Kindom', useEffect(() => {
typeItem: 'Residence', fetchWrapper
usersImges: [ .get(`data/shared/${id}`)
{ .then((res) => setEncryptedData(res.data ?? []))
size: 32, .catch((err) => console.log(err));
img: User06, }, [id]);
imgId: "90"
}, const decryptedData = useMemo(() => {
{ return encryptedData.map((item) => {
size: 32, let seed, value, hash;
img: User08, try {
imgId: "93" [seed, value] = cryptography
}, .decryptMessageWithPassphrase(
{ item.value,
size: 32, item.value_nonce,
img: User09, passPhrase,
imgId: "89" Buffer.from(pubKey, 'hex')
}, )
], .split(':');
}, hash = cryptography
{ .hash(
dataShared: localStorage.getItem('digitalId'), Buffer.concat([
id: '401', Buffer.from(seed, 'utf8'),
text: 'A123B3143', cryptography.hash(value, 'utf8'),
typeItem: 'Document ID', ])
usersImges: [ )
{ .toString('hex');
size: 32, } catch (err) {
img: User06, setHasError(true);
imgId: "67" console.log(err);
}, } finally {
{ return {
size: 32, seed,
img: User08, value,
imgId: "71" hash,
}, label: item.label,
],
},
]
}; };
}
});
}, [encryptedData]);
const validateAccountData = () => {
const builder = generateTransaction(
BigInt(store.accountInfo?.sequence?.nonce || 0),
store.pubKey,
store.nodeInfo.networkIdentifier,
store.passPhrase,
store.nodeInfo?.genesisConfig?.minFeePerByte,
store.nodeInfo?.genesisConfig?.baseFees
);
const signedTx = builder.validate(
decryptedData.map((e) => ({
label: e.label,
value: Buffer.from(e.hash, 'hex'),
})),
address
);
if (signedTx) {
fetchWrapper
.post('transactions', {}, signedTx)
.catch((err) => console.log(err));
}
};
if (!passPhrase) return <Navigate to="/" replace={true} />;
return ( return (
<main className="bg-white"> <main className="bg-white">
<div className="relative flex"> <div className="relative flex">
{/* Content */} {/* Content */}
<div className="w-full md:w-1/2"> <div className="w-full md:w-1/2">
<div className="min-h-screen h-full flex flex-col after:flex-1"> <div className="min-h-screen h-full flex flex-col after:flex-1">
<div className="px-4 mt-16 py-9"> <div className="px-4 mt-16 py-9">
<div className=" flex flex-col max-w-md mx-auto"> <div className=" flex flex-col max-w-md mx-auto">
<h1 className="text-3xl text-slate-800 font-bold mb-8">Shared data </h1> <h1 className="text-3xl text-slate-800 font-bold mb-8">
Shared data
</h1>
<ul> <ul>
{sharedDataItems.items.map(item => { {!hasError &&
return( decryptedData.map((item) => (
<li key={item.id}> <li key={item.label}>
<SharedDataRoadMap data={item}/> <SharedDataRoadMap data={item} />
</li> </li>
); ))}
})} {hasError &&
encryptedData.map((item) => (
<li key={item.label}>
<SharedDataRoadMap data={item} />
</li>
))}
</ul> </ul>
{!hasError && (
<button
className="mt-12 btn bg-indigo-500 hover:bg-indigo-600 text-white"
onClick={validateAccountData}
>
Validate data
</button>
)}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Image */} {/* Image */}
<div className="flex flex-col items-center h-full w-full hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2" aria-hidden="true"> <div
className="flex flex-col items-center h-full w-full hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2"
aria-hidden="true"
>
<div className="flex mt-40 flex-col items-center gap-2.5"> <div className="flex mt-40 flex-col items-center gap-2.5">
<img className="object-cover object-center" src={generateSvgAvatar()} width="493px" height="493px" alt="Onboarding" /> <img
<span className="text-sm">{sharedDataItems.data}</span> className="object-cover object-center"
<Link className="mt-12 btn bg-indigo-500 hover:bg-indigo-600 text-white" to="/dashboard">Get my digital ID</Link> src={generateSvgAvatar(store.pubKey)}
width="493px"
height="493px"
alt="Onboarding"
/>
<Link
className="mt-12 btn bg-indigo-500 hover:bg-indigo-600 text-white"
to="/"
>
Get my digital ID
</Link>
</div> </div>
</div> </div>
</div> </div>
</main> </main>
); );
} }

View File

@@ -1,73 +1,84 @@
import React, {useState} from 'react'; import React, { useState } from 'react';
import {observer} from "mobx-react-lite"; import { observer } from 'mobx-react-lite';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg"; import { generateSvgAvatar } from '../images/GenerateOnboardingSvg/GenerateSvg';
import Logo from "../images/logo.png"; import Logo from '../images/logo.png';
import {registrationStore} from "../store/store"; import { store } from '../store/store';
const SignIn = observer(()=>{ const SignIn = observer(() => {
const [passPhrase, setPassPhrase] = useState('- - - - - - - - - - - -');
const [addedPassPhrase, setAddedPassPhrase] = useState(false);
const[passPhrase, setPassPhrase] = useState('- - - - - - - - - - - -'); function savePassPhrase(copiedPhrase) {
const[addedPassPhrase, setAddedPassPhrase] = useState(false);
function savePassPhrase (copiedPhrase) {
if (copiedPhrase.split(' ').length === 12) { if (copiedPhrase.split(' ').length === 12) {
registrationStore.savePastPassPhrase(copiedPhrase.trim()); store.savePastPassPhrase(copiedPhrase.trim());
setPassPhrase(registrationStore.passPhrase); setPassPhrase(store.passPhrase);
setAddedPassPhrase(true); setAddedPassPhrase(true);
} }
}; }
function convertPassPhraseToArray() { function convertPassPhraseToArray() {
return passPhrase.split(' ').map((item, index) => ({str:item, id:index})); return passPhrase
.split(' ')
.map((item, index) => ({ str: item, id: index }));
} }
function pastePassPhrase() { function pastePassPhrase() {
navigator.clipboard.readText().then(res => savePassPhrase(res)); navigator.clipboard.readText().then((res) => savePassPhrase(res));
}; }
return ( return (
<main className="bg-white"> <main className="bg-white">
<div className="relative flex"> <div className="relative flex">
{/* Content */} {/* Content */}
<div className="w-full md:w-1/2"> <div className="w-full md:w-1/2">
<div className="min-h-screen h-full flex flex-col after:flex-1"> <div className="min-h-screen h-full flex flex-col after:flex-1">
<div className="flex-1"> <div className="flex-1">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8"> <div className="flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8">
{/* Logo */} {/* Logo */}
<Link className="block" to="/dashboard"> <Link className="block" to="/">
<img alt='logo' src={Logo} width="89" height="32"/> <img alt="logo" src={Logo} width="89" height="32" />
</Link> </Link>
<div className="text-sm"> <div className="text-sm">
Have not an account? <Link className="font-medium text-indigo-500 hover:text-indigo-600" to="/">Sign Up</Link> Have not an account?{' '}
<Link
className="font-medium text-indigo-500 hover:text-indigo-600"
to="/"
>
Sign Up
</Link>
</div> </div>
</div> </div>
</div> </div>
<div className="px-4 mt-20 py-8"> <div className="px-4 mt-20 py-8">
<div className="max-w-fit mx-auto flex flex-col gap-y-36"> <div className="max-w-fit mx-auto flex flex-col gap-y-36">
<div> <div>
<h1 className="text-3xl text-slate-800 font-bold mb-12">Company information </h1> <h1 className="text-3xl text-slate-800 font-bold mb-12">
Company information
</h1>
{/* htmlForm */} {/* htmlForm */}
<div className="grid grid-cols-4 gap-y-2.5 gap-[18px] mb-14"> <div className="grid grid-cols-4 gap-y-2.5 gap-[18px] mb-14">
{convertPassPhraseToArray().map(tag => ( {convertPassPhraseToArray().map((tag) => (
<button key={tag.id} className="min-w-[93px] min-h-[26px] bg-slate-100 text-slate-500 cursor-pointer hover:bg-blue-100 hover:text-blue-600 rounded-full text-center px-2.5"> <button
key={tag.id}
className="min-w-[93px] min-h-[26px] bg-slate-100 text-slate-500 cursor-pointer hover:bg-blue-100 hover:text-blue-600 rounded-full text-center px-2.5"
>
<span className="text-sm font-medium">{tag.str}</span> <span className="text-sm font-medium">{tag.str}</span>
</button> </button>
))} ))}
</div> </div>
<div className="flex pl-7 gap-x-5"> <div className="flex pl-7 gap-x-5">
<button onClick={pastePassPhrase} className="btn pl-9 pr-16 border-slate-200 hover:border-slate-300 text-slate-600"> <button
<svg className="w-4 h-4 fill-current text-slate-400 shrink-0 mb-0.5" viewBox="0 0 16 16"> onClick={pastePassPhrase}
<path className="btn pl-9 pr-16 border-slate-200 hover:border-slate-300 text-slate-600"
d="M11.7.3c-.4-.4-1-.4-1.4 0l-10 10c-.2.2-.3.4-.3.7v4c0 .6.4 1 1 1h4c.3 0 .5-.1.7-.3l10-10c.4-.4.4-1 0-1.4l-4-4zM4.6 14H2v-2.6l6-6L10.6 8l-6 6zM12 6.6L9.4 4 11 2.4 13.6 5 12 6.6z"></path> >
<svg
className="w-4 h-4 fill-current text-slate-400 shrink-0 mb-0.5"
viewBox="0 0 16 16"
>
<path d="M11.7.3c-.4-.4-1-.4-1.4 0l-10 10c-.2.2-.3.4-.3.7v4c0 .6.4 1 1 1h4c.3 0 .5-.1.7-.3l10-10c.4-.4.4-1 0-1.4l-4-4zM4.6 14H2v-2.6l6-6L10.6 8l-6 6zM12 6.6L9.4 4 11 2.4 13.6 5 12 6.6z"></path>
</svg> </svg>
<span className="ml-2">Paste</span> <span className="ml-2">Paste</span>
</button> </button>
@@ -75,29 +86,37 @@ const SignIn = observer(()=>{
</div> </div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white ml-auto"> <button className="btn bg-indigo-500 hover:bg-indigo-600 text-white ml-auto">
<Link to={addedPassPhrase && "/dashboard"}>Go To Profile -&gt;</Link> <Link to={addedPassPhrase && '/digitalId/profile-id'}>
Go To Profile -&gt;
</Link>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Image */} {/* Image */}
<div className="flex flex-col items-center h-full w-full hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2" aria-hidden="true"> <div
className="flex flex-col items-center h-full w-full hidden md:block absolute top-0 bottom-0 right-0 md:w-1/2"
aria-hidden="true"
>
<div className="flex mt-40 flex-col items-center gap-2.5"> <div className="flex mt-40 flex-col items-center gap-2.5">
<div className={`${addedPassPhrase || 'opacity-0'} w-[493px] h-[493px]`}> <div
<img className="object-cover object-center" alt="" src={generateSvgAvatar(registrationStore.pubKey)}/> className={`${
addedPassPhrase || 'opacity-0'
} w-[493px] h-[493px]`}
>
<img
className="object-cover object-center"
alt=""
src={generateSvgAvatar(store.pubKey)}
/>
</div> </div>
<span className="text-sm">Your Digital ID</span> <span className="text-sm">Your Digital ID</span>
</div> </div>
</div> </div>
</div> </div>
</main> </main>
); );
}); });

View File

@@ -1,151 +0,0 @@
import React, { useState } from 'react';
import Sidebar from '../../partials/Sidebar';
import Header from '../../partials/Header';
import AccordionBasic from '../../components/AccordionBasic';
import AccordionTableItem from '../../components/AccordionTableItem';
import AccordionTableRichItem from '../../components/AccordionTableRichItem';
import Image01 from '../../images/user-40-07.jpg';
function AccordionPage() {
const [sidebarOpen, setSidebarOpen] = useState(false);
const items = [
{
id: '0',
image: Image01,
customer: 'Mark Cameron',
total: '$129.00',
status: 'Refunded',
items: '1',
location: '🇲🇽 New Mexico, MX',
type: 'Subscription',
description: 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
},
];
const items2 = [
{
id: '0',
image: Image01,
customer: 'Mark Cameron',
email: 'mark.cameron@app.com',
location: '🇬🇧 London, UK',
date: '22/01/2021',
amount: '+249.88',
descriptionTitle: 'Excepteur sint occaecat cupidatat.',
descriptionBody: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam quis. Ut enim ad minim veniam quis. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
},
];
return (
<div className="flex h-screen overflow-hidden">
{/* Sidebar */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* Content area */}
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden bg-white">
{/* Site header */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<main>
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
{/* Page header */}
<div className="mb-8">
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">Accordion </h1>
</div>
<div className="border-t border-slate-200">
{/* Components */}
<div className="space-y-8 mt-8">
{/* Basic Accordion */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Basic Accordion</h2>
<AccordionBasic title="Accordion Title">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam quis. Ut enim ad minim veniam quis. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</AccordionBasic>
</div>
{/* Table Row with Accordion */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Table Row with Accordion</h2>
{/* Start */}
<div className="rounded-sm border border-slate-200">
<div className="overflow-x-auto">
<table className="table-auto w-full divide-y divide-slate-200">
{
items.map(item => {
return (
<AccordionTableItem
key={item.id}
id={item.id}
image={item.image}
customer={item.customer}
total={item.total}
status={item.status}
items={item.items}
location={item.location}
type={item.type}
description={item.description}
/>
)
})
}
</table>
</div>
</div>
{/* End */}
</div>
{/* Rich Table Row with Accordion */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Rich Table Row with Accordion</h2>
{/* Start */}
<div className="rounded-sm border border-slate-200">
<div className="overflow-x-auto">
<table className="table-auto w-full divide-y divide-slate-200">
{
items2.map(item => {
return (
<AccordionTableRichItem
key={item.id}
id={item.id}
image={item.image}
customer={item.customer}
email={item.email}
location={item.location}
date={item.date}
amount={item.amount}
descriptionTitle={item.descriptionTitle}
descriptionBody={item.descriptionBody}
/>
)
})
}
</table>
</div>
</div>
{/* End */}
</div>
</div>
</div>
</div>
</main>
</div>
</div>
);
}
export default AccordionPage;

View File

@@ -1,186 +0,0 @@
import React, { useState } from 'react';
import Sidebar from '../../partials/Sidebar';
import Header from '../../partials/Header';
function BadgePage() {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<div className="flex h-screen overflow-hidden">
{/* Sidebar */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* Content area */}
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden bg-white">
{/* Site header */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<main>
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
{/* Page header */}
<div className="mb-8">
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">Badge </h1>
</div>
<div className="border-t border-slate-200">
{/* Components */}
<div className="space-y-8 mt-8">
{/* Basic Small */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Basic Small</h2>
<div className="flex flex-wrap items-center -m-1.5">
<div className="m-1.5">
{/* Start */}
<div className="text-xs inline-flex font-medium bg-indigo-100 text-indigo-600 rounded-full text-center px-2.5 py-1">Working on</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-xs inline-flex font-medium bg-sky-100 text-sky-600 rounded-full text-center px-2.5 py-1">Exciting news</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-xs inline-flex font-medium bg-emerald-100 text-emerald-600 rounded-full text-center px-2.5 py-1">Product</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-xs inline-flex font-medium bg-amber-100 text-amber-600 rounded-full text-center px-2.5 py-1">Announcement</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-xs inline-flex font-medium bg-rose-100 text-rose-600 rounded-full text-center px-2.5 py-1">Bug Fix</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-xs inline-flex font-medium bg-blue-100 text-blue-600 rounded-full text-center px-2.5 py-1">Customer Stories</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-xs inline-flex font-medium bg-slate-100 text-slate-500 rounded-full text-center px-2.5 py-1">All Stories</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-xs inline-flex font-medium bg-slate-700 text-slate-100 rounded-full text-center px-2.5 py-1">All Stories</div>
{/* End */}
</div>
</div>
</div>
{/* Basic Large */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Basic Large</h2>
<div className="flex flex-wrap items-center -m-1.5">
<div className="m-1.5">
{/* Start */}
<div className="text-sm inline-flex font-medium bg-indigo-100 text-indigo-600 rounded-full text-center px-2.5 py-1">Working on</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-sm inline-flex font-medium bg-sky-100 text-sky-600 rounded-full text-center px-2.5 py-1">Exciting news</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-sm inline-flex font-medium bg-emerald-100 text-emerald-600 rounded-full text-center px-2.5 py-1">Product</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-sm inline-flex font-medium bg-amber-100 text-amber-600 rounded-full text-center px-2.5 py-1">Announcement</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-sm inline-flex font-medium bg-rose-100 text-rose-600 rounded-full text-center px-2.5 py-1">Bug Fix</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-sm inline-flex font-medium bg-blue-100 text-blue-600 rounded-full text-center px-2.5 py-1">Customer Stories</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-sm inline-flex font-medium bg-slate-100 text-slate-500 rounded-full text-center px-2.5 py-1">All Stories</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-sm inline-flex font-medium bg-slate-700 text-slate-100 rounded-full text-center px-2.5 py-1">All Stories</div>
{/* End */}
</div>
</div>
</div>
{/* Basic with Icon */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Basic with Icon</h2>
<div className="flex flex-wrap items-center -m-1.5">
<div className="m-1.5">
{/* Start */}
<div className="inline-flex items-center text-xs font-medium text-slate-100 bg-slate-700 rounded-full text-center px-2 py-0.5">
<svg className="w-3 h-3 shrink-0 fill-current text-amber-500 mr-1" viewBox="0 0 12 12">
<path d="M11.953 4.29a.5.5 0 00-.454-.292H6.14L6.984.62A.5.5 0 006.12.173l-6 7a.5.5 0 00.379.825h5.359l-.844 3.38a.5.5 0 00.864.445l6-7a.5.5 0 00.075-.534z" />
</svg>
<span>Special Offer</span>
</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="inline-flex items-center text-sm font-medium text-slate-100 bg-slate-700 rounded-full text-center px-2 py-0.5">
<svg className="w-3 h-3 shrink-0 fill-current text-amber-500 mr-1" viewBox="0 0 12 12">
<path d="M11.953 4.29a.5.5 0 00-.454-.292H6.14L6.984.62A.5.5 0 006.12.173l-6 7a.5.5 0 00.379.825h5.359l-.844 3.38a.5.5 0 00.864.445l6-7a.5.5 0 00.075-.534z" />
</svg>
<span>Special Offer</span>
</div>
{/* End */}
</div>
</div>
</div>
{/* Basic for Charts */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Basic for Charts</h2>
<div className="flex flex-wrap items-center -m-1.5">
<div className="m-1.5">
{/* Start */}
<div className="text-sm font-semibold text-white px-1.5 bg-emerald-500 rounded-full">+29%</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="text-sm font-semibold text-white px-1.5 bg-amber-500 rounded-full">-14%</div>
{/* End */}
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
);
}
export default BadgePage;

View File

@@ -1,235 +0,0 @@
import React, { useState } from 'react';
import Sidebar from '../../partials/Sidebar';
import Header from '../../partials/Header';
function ButtonPage() {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<div className="flex h-screen overflow-hidden">
{/* Sidebar */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* Content area */}
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden bg-white">
{/* Site header */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<main>
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
{/* Page header */}
<div className="mb-8">
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">Button </h1>
</div>
<div className="border-t border-slate-200">
{/* Components */}
<div className="space-y-8 mt-8">
{/* Appearances */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Appearances</h2>
<div className="flex flex-wrap items-center -m-1.5">
<div className="m-1.5">
{/* Start */}
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white">Primary</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn border-slate-200 hover:border-slate-300 text-indigo-500">Secondary</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn border-slate-200 hover:border-slate-300 text-slate-600">Tertiary</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn bg-rose-500 hover:bg-rose-600 text-white">Danger</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn border-slate-200 hover:border-slate-300 text-rose-500">Danger</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn bg-emerald-500 hover:bg-emerald-600 text-white">Success</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn border-slate-200 hover:border-slate-300 text-emerald-500">Success</button>
{/* End */}
</div>
</div>
</div>
{/* States */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">States</h2>
<div className="flex flex-wrap items-center -m-1.5">
<div className="m-1.5">
{/* Start */}
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white disabled:border-slate-200 disabled:bg-slate-100 disabled:text-slate-400 disabled:cursor-not-allowed shadow-none" disabled>Disabled</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white disabled:border-slate-200 disabled:bg-slate-100 disabled:text-slate-400 disabled:cursor-not-allowed shadow-none" disabled>
<svg className="animate-spin w-4 h-4 fill-current shrink-0" viewBox="0 0 16 16">
<path d="M8 16a7.928 7.928 0 01-3.428-.77l.857-1.807A6.006 6.006 0 0014 8c0-3.309-2.691-6-6-6a6.006 6.006 0 00-5.422 8.572l-1.806.859A7.929 7.929 0 010 8c0-4.411 3.589-8 8-8s8 3.589 8 8-3.589 8-8 8z" />
</svg>
<span className="ml-2">Loading</span>
</button>
{/* End */}
</div>
</div>
</div>
{/* Sizes */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Sizes</h2>
<div className="flex flex-wrap items-center -m-1.5">
<div className="m-1.5">
{/* Start */}
<button className="btn-xs bg-indigo-500 hover:bg-indigo-600 text-white">Button</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn-sm bg-indigo-500 hover:bg-indigo-600 text-white">Button</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white">Button</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn-lg bg-indigo-500 hover:bg-indigo-600 text-white">Button</button>
{/* End */}
</div>
</div>
</div>
{/* Buttons with an Icon */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Buttons with an Icon</h2>
<div className="flex flex-wrap items-center -m-1.5">
<div className="m-1.5">
{/* Start */}
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white">
<svg className="w-4 h-4 fill-current opacity-50 shrink-0" viewBox="0 0 16 16">
<path d="M15 7H9V1c0-.6-.4-1-1-1S7 .4 7 1v6H1c-.6 0-1 .4-1 1s.4 1 1 1h6v6c0 .6.4 1 1 1s1-.4 1-1V9h6c.6 0 1-.4 1-1s-.4-1-1-1z" />
</svg>
<span className="ml-2">Add Event</span>
</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn border-slate-200 hover:border-slate-300 text-slate-600">
<svg className="w-4 h-4 fill-current text-slate-500 shrink-0" viewBox="0 0 16 16">
<path d="M11.7.3c-.4-.4-1-.4-1.4 0l-10 10c-.2.2-.3.4-.3.7v4c0 .6.4 1 1 1h4c.3 0 .5-.1.7-.3l10-10c.4-.4.4-1 0-1.4l-4-4zM4.6 14H2v-2.6l6-6L10.6 8l-6 6zM12 6.6L9.4 4 11 2.4 13.6 5 12 6.6z" />
</svg>
<span className="ml-2">Edit Content</span>
</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn border-slate-200 hover:border-slate-300 text-rose-500">
<svg className="w-4 h-4 fill-current shrink-0" viewBox="0 0 16 16">
<path d="M5 7h2v6H5V7zm4 0h2v6H9V7zm3-6v2h4v2h-1v10c0 .6-.4 1-1 1H2c-.6 0-1-.4-1-1V5H0V3h4V1c0-.6.4-1 1-1h6c.6 0 1 .4 1 1zM6 2v1h4V2H6zm7 3H3v9h10V5z" />
</svg>
<span className="ml-2">Delete</span>
</button>
{/* End */}
</div>
</div>
</div>
{/* Icon Buttons */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Icon Buttons</h2>
<div className="flex flex-wrap items-center -m-1.5">
<div className="m-1.5">
{/* Start */}
<button className="btn border-slate-200 hover:border-slate-300">
<svg className="w-4 h-4 fill-current text-slate-500 shrink-0" viewBox="0 0 16 16">
<path d="M11.7.3c-.4-.4-1-.4-1.4 0l-10 10c-.2.2-.3.4-.3.7v4c0 .6.4 1 1 1h4c.3 0 .5-.1.7-.3l10-10c.4-.4.4-1 0-1.4l-4-4zM4.6 14H2v-2.6l6-6L10.6 8l-6 6zM12 6.6L9.4 4 11 2.4 13.6 5 12 6.6z" />
</svg>
</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn border-slate-200 hover:border-slate-300">
<svg className="w-4 h-4 fill-current text-rose-500 shrink-0" viewBox="0 0 16 16">
<path d="M5 7h2v6H5V7zm4 0h2v6H9V7zm3-6v2h4v2h-1v10c0 .6-.4 1-1 1H2c-.6 0-1-.4-1-1V5H0V3h4V1c0-.6.4-1 1-1h6c.6 0 1 .4 1 1zM6 2v1h4V2H6zm7 3H3v9h10V5z" />
</svg>
</button>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<button className="btn border-slate-200 hover:border-slate-300">
<svg className="w-4 h-4 fill-current text-indigo-500 shrink-0" viewBox="0 0 16 16">
<path d="M14.3 2.3L5 11.6 1.7 8.3c-.4-.4-1-.4-1.4 0-.4.4-.4 1 0 1.4l4 4c.2.2.4.3.7.3.3 0 .5-.1.7-.3l10-10c.4-.4.4-1 0-1.4-.4-.4-1-.4-1.4 0z" />
</svg>
</button>
{/* End */}
</div>
</div>
</div>
{/* Button Groups */}
<div>
<h2 className="text-2xl text-slate-800 font-bold mb-6">Button Groups</h2>
<div className="flex flex-wrap items-center -m-1.5">
<div className="m-1.5">
{/* Start */}
<div className="flex flex-wrap -space-x-px">
<button className="btn bg-indigo-600 text-white rounded-none border-l-indigo-400 first:rounded-l last:rounded-r first:border-r-transparent">Weekly</button>
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-indigo-100 rounded-none border-l-indigo-400 first:rounded-l last:rounded-r first:border-r-transparent">Monthly</button>
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-indigo-100 rounded-none border-l-indigo-400 first:rounded-l last:rounded-r first:border-r-transparent">Yearly</button>
</div>
{/* End */}
</div>
<div className="m-1.5">
{/* Start */}
<div className="flex flex-wrap -space-x-px">
<button className="btn bg-slate-50 border-slate-200 hover:bg-slate-50 text-indigo-500 rounded-none first:rounded-l last:rounded-r">Weekly</button>
<button className="btn bg-white border-slate-200 hover:bg-slate-50 text-slate-600 rounded-none first:rounded-l last:rounded-r">Monthly</button>
<button className="btn bg-white border-slate-200 hover:bg-slate-50 text-slate-600 rounded-none first:rounded-l last:rounded-r">Yearly</button>
</div>
{/* End */}
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
);
}
export default ButtonPage;

View File

@@ -1,14 +1,25 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { registrationStore } from "../../store/store"; import { store } from '../../store/store';
import Sidebar from '../../partials/Sidebar'; import Sidebar from '../../partials/Sidebar';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import Header from '../../partials/Header'; import Header from '../../partials/Header';
import ProfileTable from '../../partials/profile/ProfileTable'; import ProfileTable from '../../partials/profile/ProfileTable';
import Image from '../../images/transactions-image-04.svg'; import Image from '../../images/transactions-image-04.svg';
import { labelMap } from '../../shared/labelMap';
import { statusMap } from '../../shared/statusMap';
const initialPropertyValues = ['First name', 'Second name', 'Birthdate', 'Gender', 'National doctype', 'National doc ID', 'National doc issue date', 'National doc expiry date']; const initialPropertyValues = [
'First name',
'Second name',
'Birthdate',
'Gender',
'National doctype',
'National doc ID',
'National doc issue date',
'National doc expiry date',
];
const Profile = observer (() => { const Profile = observer(() => {
const defaultValues = { const defaultValues = {
label: '', label: '',
value: '', value: '',
@@ -19,25 +30,29 @@ const Profile = observer (() => {
const [buttonPanelOpen, setButtonPanelOpen] = useState(true); const [buttonPanelOpen, setButtonPanelOpen] = useState(true);
const [removePanelOpen, setRemovePanelOpen] = useState(false); const [removePanelOpen, setRemovePanelOpen] = useState(false);
const [changePanelOpen, setChangePanelOpen] = useState(false); const [changePanelOpen, setChangePanelOpen] = useState(false);
const [toggle, setToggle] = useState(true); const [storeOnBlockchain, setStoreOnBlockchain] = useState(false);
const [addPanelOpen, setAddPanelOpen] = useState(false); const [addPanelOpen, setAddPanelOpen] = useState(false);
const [sharePanelOpen, setSharePanelOpen] = useState(false);
const [updatePanelOpen, setUpdatePanelOpen] = useState(false); const [updatePanelOpen, setUpdatePanelOpen] = useState(false);
const [selectedItems, setSelectedItems] = useState([]); const [selectedItems, setSelectedItems] = useState([]);
const [propertyValues, setPropertyValues] = useState([]); const [propertyValues, setPropertyValues] = useState([]);
const [updatedValues, setUpdatedValues] = useState([defaultValues]); const [updatedValues, setUpdatedValues] = useState([defaultValues]);
const [addedValues, setAddedValues] = useState(defaultValues); const [addedValues, setAddedValues] = useState(defaultValues);
const [isCheck, setIsCheck] = useState([]); const [isCheck, setIsCheck] = useState([]);
const [alreadyExists, setAlreadyExists] = useState(false);
const [blockChainValue, setBlockChainValue] = useState('');
const [publicKey, setPublicKey] = useState('');
useEffect(() => { useEffect(() => {
handleSelectedItems(isCheck); handleSelectedItems(isCheck);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [isCheck]); }, [isCheck]);
const handleClick = e => { const handleClick = (e) => {
const { id, checked } = e.target; const { id, checked } = e.target;
setIsCheck([...isCheck, id]); setIsCheck([...isCheck, id]);
if (!checked || !id) { if (!checked || !id) {
setIsCheck(isCheck.filter(item => item !== id)); setIsCheck(isCheck.filter((item) => item !== id));
} }
}; };
@@ -51,24 +66,29 @@ const Profile = observer (() => {
setAddedValues((prevState) => ({ setAddedValues((prevState) => ({
...prevState, ...prevState,
[field]: value, [field]: value,
})) }));
}; };
const changeUpdatedValues = (value, field, key) => { const changeUpdatedValues = (value, field, key) => {
if (field === 'value') {
setBlockChainValue(value);
}
setUpdatedValues((prevState) => setUpdatedValues((prevState) =>
prevState.map((elem) => ( prevState.map((elem) =>
(elem.key === key) ? { elem.key === key
? {
...elem, ...elem,
[field]: value, [field]: value,
} : elem }
)) : elem
) )
);
}; };
const selectHint = (element) => { const selectHint = (element) => {
changeAddedValues(element, 'label'); changeAddedValues(element, 'label');
setPropertyValues([]); setPropertyValues([]);
} };
const closeHint = (eventTarget) => { const closeHint = (eventTarget) => {
if (!eventTarget || eventTarget.tabIndex !== -1) { if (!eventTarget || eventTarget.tabIndex !== -1) {
@@ -78,12 +98,21 @@ const Profile = observer (() => {
const handleSelectedItems = (selectedItems) => { const handleSelectedItems = (selectedItems) => {
setSelectedItems([...selectedItems]); setSelectedItems([...selectedItems]);
setUpdatedValues(registrationStore.decryptedAccountData.filter(({ key }) => selectedItems.includes(key)) setUpdatedValues(
.map(elem => ( store.decryptedAccountData
(!elem.seed) ? { .filter(({ key }) => selectedItems.includes(key))
.map((elem) =>
!elem.seed
? {
...elem, ...elem,
seed: String(Math.floor(Math.random() * 90000000000000000000), 10), seed: String(
}: elem))); Math.floor(Math.random() * 90000000000000000000),
10
),
}
: elem
)
);
if (selectedItems.length > 0) { if (selectedItems.length > 0) {
setButtonPanelOpen(false); setButtonPanelOpen(false);
setAddPanelOpen(false); setAddPanelOpen(false);
@@ -106,8 +135,10 @@ const Profile = observer (() => {
}; };
const sendAddedData = () => { const sendAddedData = () => {
const checkingAddedData = !registrationStore.decryptedAccountData const checkingAddedData = !store.decryptedAccountData.some(
.some((element) => element.label === addedValues.label) && (addedValues.seed.length === 20); (element) => element.label === addedValues.label
);
setAlreadyExists(!checkingAddedData);
if (checkingAddedData) { if (checkingAddedData) {
setAddPanelOpen(false); setAddPanelOpen(false);
setButtonPanelOpen(true); setButtonPanelOpen(true);
@@ -124,37 +155,65 @@ const Profile = observer (() => {
}; };
const deleteDataParameters = () => { const deleteDataParameters = () => {
const changeData = registrationStore.decryptedAccountData.filter(item=>!selectedItems.includes(item.key)) const changeData = store.decryptedAccountData.filter(
registrationStore.pushAccountData(changeData); (item) => !selectedItems.includes(item.key)
);
store.pushAccountData(changeData);
if (storeOnBlockchain)
store.pushAccountDataToBlockchain(
changeData.filter((elem) => elem.status !== 'Stored')
);
}; };
const changeInitialArray = () => { const changeInitialArray = () => {
let newArr = []; let newArr = [];
registrationStore.decryptedAccountData.map(elem => { store.decryptedAccountData.map((elem) => {
updatedValues.forEach(item => { updatedValues.forEach((item) => {
if (elem.label === item.label) { if (elem.label === item.label) {
newArr.push({ newArr.push({
...elem, ...elem,
'value': item.value, status: 'new',
'seed': item.seed, value: item.value,
}) seed: item.seed,
});
} }
}) });
if (!newArr.find(element => element.label === elem.label)) { if (!newArr.find((element) => element.label === elem.label)) {
newArr.push(elem); newArr.push(elem);
} }
}) });
return newArr; return newArr;
}; };
const changeDataParameters = () => { const changeDataParameters = () => {
const updatedData = changeInitialArray(); const updatedData = changeInitialArray();
registrationStore.pushAccountData(updatedData); store.pushAccountData(updatedData);
if (storeOnBlockchain)
store.pushAccountDataToBlockchain(
updatedData.filter((elem) => elem.status !== 'Stored')
);
};
const shareAccountData = () => {
const sharedData = store.decryptedAccountData.filter((item) =>
selectedItems.includes(item.key)
);
store.pushSharedData(sharedData, publicKey);
cancelSharePanel();
}; };
const addDataParameters = () => { const addDataParameters = () => {
const label = addedValues.label.toLowerCase().split(' ').join('');
if (labelMap[label])
addedValues.key = addedValues.label.toLowerCase().split(' ').join(''); addedValues.key = addedValues.label.toLowerCase().split(' ').join('');
registrationStore.pushAccountData(registrationStore.decryptedAccountData.concat(addedValues)); else addedValues.key = addedValues.label;
store.pushAccountData(store.decryptedAccountData.concat(addedValues));
if (storeOnBlockchain)
store.pushAccountDataToBlockchain(
store.decryptedAccountData
.concat(addedValues)
.filter((elem) => elem.status !== 'Stored')
);
}; };
const cancelAddPanel = () => { const cancelAddPanel = () => {
@@ -164,12 +223,21 @@ const Profile = observer (() => {
}; };
const cancelUpdatePanel = () => { const cancelUpdatePanel = () => {
setUpdatedValues(registrationStore.decryptedAccountData.filter(({ key }) => selectedItems.includes(key)) setUpdatedValues(
.map(elem => ( store.decryptedAccountData
(!elem.seed) ? { .filter(({ key }) => selectedItems.includes(key))
.map((elem) =>
!elem.seed
? {
...elem, ...elem,
seed: String(Math.floor(Math.random() * 90000000000000000000), 10), seed: String(
}: elem))); Math.floor(Math.random() * 90000000000000000000),
10
),
}
: elem
)
);
setUpdatePanelOpen(false); setUpdatePanelOpen(false);
setChangePanelOpen(true); setChangePanelOpen(true);
setIsCheck([]); setIsCheck([]);
@@ -180,6 +248,16 @@ const Profile = observer (() => {
setChangePanelOpen(false); setChangePanelOpen(false);
}; };
const openSharePanel = () => {
setSharePanelOpen(true);
setChangePanelOpen(false);
};
const cancelSharePanel = () => {
setSharePanelOpen(false);
setChangePanelOpen(true);
};
const openUpdatePanel = () => { const openUpdatePanel = () => {
setUpdatePanelOpen(true); setUpdatePanelOpen(true);
setChangePanelOpen(false); setChangePanelOpen(false);
@@ -197,7 +275,9 @@ const Profile = observer (() => {
setPropertyValues(initialPropertyValues); setPropertyValues(initialPropertyValues);
if (text) { if (text) {
let reg = new RegExp(`^${text}`, 'img'); let reg = new RegExp(`^${text}`, 'img');
setPropertyValues((prevState) => prevState.filter((element) => element.match(reg))); setPropertyValues((prevState) =>
prevState.filter((element) => element.match(reg))
);
} }
}; };
@@ -234,20 +314,27 @@ const Profile = observer (() => {
</ul> </ul>
</div> </div>
{/* Table */} {/* Table */}
<div className='w-[828px]'> <div>
<ProfileTable isCheck={isCheck} handleClick={handleClick} userData={registrationStore.decryptedAccountData}/> <ProfileTable
isCheck={isCheck}
handleClick={handleClick}
userData={store.decryptedAccountData}
/>
</div> </div>
</div> </div>
{/* Left sidebar */} {/* Left sidebar */}
<div> <div>
{/* Button panel*/} {/* Button panel*/}
<div className={`${!buttonPanelOpen && "hidden"} ml-2.5`}> <div className={`${!buttonPanelOpen && 'hidden'} ml-2.5`}>
<div className="bg-white px-5 py-[43px] shadow-lg rounded-sm border border-slate-200 lg:w-72 xl:w-80"> <div className="bg-white px-5 py-[43px] shadow-lg rounded-sm border border-slate-200 lg:w-72 xl:w-80">
<button <button
className="btn w-full bg-indigo-500 hover:bg-indigo-600 text-white" className="btn w-full bg-indigo-500 hover:bg-indigo-600 text-white"
onClick={openAddPanel} onClick={openAddPanel}
> >
<svg className="w-4 h-4 fill-current shrink-0" viewBox="0 0 16 16"> <svg
className="w-4 h-4 fill-current shrink-0"
viewBox="0 0 16 16"
>
<path d="m2.457 8.516.969-.99 2.516 2.481 5.324-5.304.985.989-6.309 6.284z" /> <path d="m2.457 8.516.969-.99 2.516 2.481 5.324-5.304.985.989-6.309 6.284z" />
</svg> </svg>
<span className="ml-1">Add new field</span> <span className="ml-1">Add new field</span>
@@ -256,13 +343,24 @@ const Profile = observer (() => {
</div> </div>
<div className="ml-2.5"> <div className="ml-2.5">
{/* Add panel */} {/* Add panel */}
<div className={`${!addPanelOpen && 'hidden'} relative flex flex-col bg-white p-5 shadow-lg rounded-sm border border-slate-200 lg:w-72 xl:w-80 mb-11`}> <div
<div className='relative z-20'> className={`${
<label className="block text-sm font-medium mb-1" htmlFor="mandatory">Property <span className="text-rose-500">*</span></label> !addPanelOpen && 'hidden'
} relative flex flex-col bg-white p-5 shadow-lg rounded-sm border border-slate-200 lg:w-72 xl:w-80 mb-11`}
>
<div className="relative z-20">
<label
className="block text-sm font-medium mb-1"
htmlFor="mandatory"
>
Property <span className="text-rose-500">*</span>
</label>
<input <input
autoComplete='off' autoComplete="off"
id="label" id="label"
className="form-input w-full" className={`form-input w-full ${
alreadyExists && 'border-rose-300'
}`}
type="text" type="text"
required required
onInput={(e) => handleInput(e.target.value, 'label')} onInput={(e) => handleInput(e.target.value, 'label')}
@@ -270,47 +368,90 @@ const Profile = observer (() => {
onClick={openHint} onClick={openHint}
onBlur={(e) => closeHint(e.relatedTarget)} onBlur={(e) => closeHint(e.relatedTarget)}
/> />
<div tabIndex='-1' className={`absolute w-full box-border bg-white flex flex-col gap-y-2 py-2 pl-2 border border-t-0 border-slate-200 rounded ${(propertyValues.length > 0) || 'hidden'}`}> <div
tabIndex="-1"
className={`absolute w-full box-border bg-white flex flex-col gap-y-2 py-2 pl-2 border border-t-0 border-slate-200 rounded ${
propertyValues.length > 0 || 'hidden'
}`}
>
<ul> <ul>
{propertyValues.map((element, index) => ( {propertyValues.map((element, index) => (
<li className='hover:text-indigo-600 cursor-pointer' key={index} onClick={() => selectHint(element)}>{element}</li> <li
className="hover:text-indigo-600 cursor-pointer"
key={index}
onClick={() => selectHint(element)}
>
{element}
</li>
))} ))}
</ul> </ul>
</div> </div>
{alreadyExists && (
<div className="text-xs mt-1 text-rose-500">
This property already added!
</div> </div>
<div className='my-3 z-10'> )}
<label className="block text-sm font-medium mb-1" htmlFor="mandatory">Value <span className="text-rose-500">*</span></label> </div>
<div className="my-3 z-10">
<label
className="block text-sm font-medium mb-1"
htmlFor="mandatory"
>
Value <span className="text-rose-500">*</span>
</label>
<input <input
autoComplete='off' autoComplete="off"
id="value" id="value"
className="form-input w-full" className="form-input w-full"
type="text" type="text"
required required
onClick={() => setPropertyValues([])} onClick={() => setPropertyValues([])}
onChange={(e) => changeAddedValues(e.target.value, 'value')} onChange={(e) =>
changeAddedValues(e.target.value, 'value')
}
value={addedValues.value} value={addedValues.value}
/> />
</div> </div>
<div className='z-10'> <div className="z-10">
<label className="block text-sm font-medium mb-1" htmlFor="mandatory">Seed <span className="text-rose-500">*</span></label> <label
className="block text-sm font-medium mb-1"
htmlFor="mandatory"
>
Seed <span className="text-rose-500">*</span>
</label>
<input <input
autoComplete='off' autoComplete="off"
id="seed" id="seed"
className="form-input w-full" className="form-input w-full"
type="text" type="text"
required required
onClick={() => setPropertyValues([])} onClick={() => setPropertyValues([])}
onChange={(e) => changeAddedValues(e.target.value, 'seed')} onChange={(e) =>
changeAddedValues(e.target.value, 'seed')
}
value={addedValues.seed} value={addedValues.seed}
/> />
</div> </div>
<div className="flex flex-row z-10 justify-between pt-[18px] pb-[23px] items-center border-b border-slate-200"> <div className="flex flex-row z-10 justify-between pt-[18px] pb-[23px] items-center border-b border-slate-200">
<span className="block text-sm font-medium">Store data on blockchain</span> <span className="block text-sm font-medium">
Store data on blockchain
</span>
<div className="flex items-center"> <div className="flex items-center">
<div className="form-switch"> <div className="form-switch">
<input type="checkbox" id="blockcheined" className="sr-only" checked={toggle} onChange={() => setToggle(!toggle)} /> <input
type="checkbox"
id="blockcheined"
className="sr-only"
checked={storeOnBlockchain}
onChange={() =>
setStoreOnBlockchain(!storeOnBlockchain)
}
/>
<label className="bg-slate-400" htmlFor="switch-1"> <label className="bg-slate-400" htmlFor="switch-1">
<span className="bg-white shadow-sm" aria-hidden="true"></span> <span
className="bg-white shadow-sm"
aria-hidden="true"
></span>
</label> </label>
</div> </div>
</div> </div>
@@ -321,7 +462,7 @@ const Profile = observer (() => {
className="btn-sm bg-white border-slate-200 hover:bg-slate-50 text-slate-600" className="btn-sm bg-white border-slate-200 hover:bg-slate-50 text-slate-600"
onClick={cancelAddPanel} onClick={cancelAddPanel}
> >
Cansel Cancel
</button> </button>
<button <button
className="btn-sm bg-indigo-500 hover:bg-indigo-600 text-white" className="btn-sm bg-indigo-500 hover:bg-indigo-600 text-white"
@@ -332,57 +473,111 @@ const Profile = observer (() => {
</div> </div>
</div> </div>
{/* Update panel */} {/* Update panel */}
<div className={`${!updatePanelOpen && 'hidden'} bg-white p-5 shadow-lg rounded-sm border border-slate-200 lg:w-72 xl:w-80 mb-11`}> <div
className={`${
!updatePanelOpen && 'hidden'
} bg-white p-5 shadow-lg rounded-sm border border-slate-200 lg:w-72 xl:w-80 mb-11`}
>
{updatedValues.map((item, index) => ( {updatedValues.map((item, index) => (
<div key={index} className="flex flex-col gap-y-3"> <div key={index} className="flex flex-col gap-y-3">
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="mandatory">Property <span className="text-rose-500">*</span></label> <label
className="block text-sm font-medium mb-1"
htmlFor="mandatory"
>
Property <span className="text-rose-500">*</span>
</label>
<input <input
id={`${item.key}:label`} id={`${item.key}:label`}
className="form-input w-full" className="form-input w-full"
type="text" type="text"
required required
onChange={(e) => changeUpdatedValues(e.target.value, 'label', item.key)} onChange={(e) =>
changeUpdatedValues(
e.target.value,
'label',
item.key
)
}
value={item.label} value={item.label}
disabled='disabled' disabled
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="mandatory">Value <span className="text-rose-500">*</span></label> <label
className="block text-sm font-medium mb-1"
htmlFor="mandatory"
>
Value <span className="text-rose-500">*</span>
</label>
<input <input
autoComplete='off' autoComplete="off"
id={`${item.key}:value`} id={`${item.key}:value`}
className="form-input w-full" className="form-input w-full"
type="text" type="text"
required required
onChange={(e) => changeUpdatedValues(e.target.value, 'value', item.key)} onChange={(e) =>
value={item.value} changeUpdatedValues(
e.target.value,
'value',
item.key
)
}
value={
item.status === statusMap.blockchained
? blockChainValue
: item.value
}
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="mandatory">Seed <span className="text-rose-500">*</span></label> <label
className="block text-sm font-medium mb-1"
htmlFor="mandatory"
>
Seed <span className="text-rose-500">*</span>
</label>
<input <input
autoComplete='off' autoComplete="off"
id={`${item.key}:seed`} id={`${item.key}:seed`}
className="form-input w-full" className="form-input w-full"
type="text" type="text"
required required
onChange={(e) => changeUpdatedValues(e.target.value, 'seed', item.key)} onChange={(e) =>
changeUpdatedValues(
e.target.value,
'seed',
item.key
)
}
value={item.seed} value={item.seed}
/> />
</div> </div>
{(updatedValues[index+1]) ? ( {updatedValues[index + 1] ? (
<div className="mt-2 mb-3.5 items-center border-b border-slate-200"></div> <div className="mt-2 mb-3.5 items-center border-b border-slate-200"></div>
) : null} ) : null}
</div> </div>
))} ))}
<div className="flex flex-row justify-between pt-[16px] pb-[23px] items-center border-b border-slate-200"> <div className="flex flex-row justify-between pt-[16px] pb-[23px] items-center border-b border-slate-200">
<span className="block text-sm font-medium">Store data on blockchain</span> <span className="block text-sm font-medium">
Store data on blockchain
</span>
<div className="flex items-center"> <div className="flex items-center">
<div className="form-switch"> <div className="form-switch">
<input type="checkbox" id="blockcheined" className="sr-only" checked={toggle} onChange={() => setToggle(!toggle)} /> <input
type="checkbox"
id="blockcheined"
className="sr-only"
checked={storeOnBlockchain}
onChange={() =>
setStoreOnBlockchain(!storeOnBlockchain)
}
/>
<label className="bg-slate-400" htmlFor="switch-1"> <label className="bg-slate-400" htmlFor="switch-1">
<span className="bg-white shadow-sm" aria-hidden="true"></span> <span
className="bg-white shadow-sm"
aria-hidden="true"
></span>
</label> </label>
</div> </div>
</div> </div>
@@ -393,7 +588,7 @@ const Profile = observer (() => {
className="btn-sm bg-white border-slate-200 hover:bg-slate-50 text-slate-600" className="btn-sm bg-white border-slate-200 hover:bg-slate-50 text-slate-600"
onClick={cancelUpdatePanel} onClick={cancelUpdatePanel}
> >
Cansel Cancel
</button> </button>
<button <button
className="btn-sm bg-indigo-500 hover:bg-indigo-600 text-white" className="btn-sm bg-indigo-500 hover:bg-indigo-600 text-white"
@@ -404,20 +599,46 @@ const Profile = observer (() => {
</div> </div>
</div> </div>
{/* Remove panel */} {/* Remove panel */}
<div className={`${!removePanelOpen && 'hidden'} bg-white px-5 pt-4 pb-[190px] shadow-lg rounded-sm border border-slate-200 lg:w-72 xl:w-80 mb-12`}> <div
<h2 className="grow text-base font-semibold text-slate-800 truncate mb-2">Summary</h2> className={`${
!removePanelOpen && 'hidden'
} bg-white px-5 pt-4 pb-[190px] shadow-lg rounded-sm border border-slate-200 lg:w-72 xl:w-80 mb-12`}
>
<h2 className="grow text-base font-semibold text-slate-800 truncate mb-2">
Summary
</h2>
<div className="flex flex-col"> <div className="flex flex-col">
{registrationStore.decryptedAccountData.filter(({ key }) => selectedItems.includes(key)).map(item => ( {store.decryptedAccountData
<span key={item.label} className="text-sm font-normal text-slate-600 py-3 border-b border-slate-200">{item.label}</span> .filter(({ key }) => selectedItems.includes(key))
.map((item) => (
<span
key={item.label}
className="text-sm font-normal text-slate-600 py-3 border-b border-slate-200"
>
{item.label}
</span>
))} ))}
</div> </div>
<div className="flex flex-row justify-between pt-[16px] pb-7 items-center"> <div className="flex flex-row justify-between pt-[16px] pb-7 items-center">
<span className="block text-sm font-medium">On blockchain too</span> <span className="block text-sm font-medium">
On blockchain too
</span>
<div className="flex items-center"> <div className="flex items-center">
<div className="form-switch"> <div className="form-switch">
<input type="checkbox" id="switch-1" className="sr-only" checked={toggle} onChange={() => setToggle(!toggle)} /> <input
type="checkbox"
id="switch-1"
className="sr-only"
checked={storeOnBlockchain}
onChange={() =>
setStoreOnBlockchain(!storeOnBlockchain)
}
/>
<label className="bg-slate-400" htmlFor="switch-1"> <label className="bg-slate-400" htmlFor="switch-1">
<span className="bg-white shadow-sm" aria-hidden="true"></span> <span
className="bg-white shadow-sm"
aria-hidden="true"
></span>
</label> </label>
</div> </div>
</div> </div>
@@ -427,35 +648,107 @@ const Profile = observer (() => {
className="btn w-full bg-indigo-500 hover:bg-indigo-600 text-white px-[100px] justify-start" className="btn w-full bg-indigo-500 hover:bg-indigo-600 text-white px-[100px] justify-start"
onClick={applyDataDeletion} onClick={applyDataDeletion}
> >
<svg className="w-4 h-4 fill-current shrink-0" viewBox="0 0 16 16"> <svg
className="w-4 h-4 fill-current shrink-0"
viewBox="0 0 16 16"
>
<path d="m2.457 8.516.969-.99 2.516 2.481 5.324-5.304.985.989-6.309 6.284z" /> <path d="m2.457 8.516.969-.99 2.516 2.481 5.324-5.304.985.989-6.309 6.284z" />
</svg> </svg>
<span className="ml-2">Apply</span> <span className="ml-2">Apply</span>
</button> </button>
</div> </div>
<div className="flex justify-center"> <div className="flex justify-center">
<span className="text-descriptionSize text-slate-500 font-normal italic text-center">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do Terms.</span> <span className="text-descriptionSize text-slate-500 font-normal italic text-center">
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do Terms.
</span>
</div>
</div>
{/* Share panel */}
<div
className={`${
!sharePanelOpen && 'hidden'
} bg-white px-5 pt-4 shadow-lg rounded-sm border border-slate-200 mb-12 lg:w-72 xl:w-80`}
>
<div className="relative z-20">
<div className="z-10">
<label
className="block text-sm font-medium mb-1"
htmlFor="mandatory"
>
Public Key <span className="text-rose-500">*</span>
</label>
<input
autoComplete="off"
id="seed"
className="form-input w-full"
type="text"
required
onChange={(e) => setPublicKey(e.target.value)}
value={publicKey}
/>
</div>
{/* Buttons */}
<div className="flex flex-row z-10 justify-end gap-x-2 pt-[18px] pb-[14px] items-end">
<button
className="btn-sm bg-white border-slate-200 hover:bg-slate-50 text-slate-600"
onClick={cancelSharePanel}
>
Cancel
</button>
<button
className="btn-sm bg-indigo-500 hover:bg-indigo-600 text-white"
onClick={shareAccountData}
>
Share
</button>
</div>
</div> </div>
</div> </div>
{/* Details */} {/* Details */}
<div className={`${(removePanelOpen || addPanelOpen) || 'hidden'} drop-shadow-lg`}> <div
className={`${
(!(removePanelOpen || addPanelOpen) ||
!storeOnBlockchain) &&
'hidden'
} drop-shadow-lg`}
>
{/* Top */} {/* Top */}
<div className="bg-white rounded-t-xl px-5 pb-4 text-center"> <div className="bg-white rounded-t-xl px-5 pb-4 text-center">
<div className="mb-3 text-center"> <div className="mb-3 text-center">
<img className="inline-flex w-12 h-12 rounded-full -mt-6" src={Image} width="48" height="48" alt="Transaction 04" /> <img
className="inline-flex w-12 h-12 rounded-full -mt-6"
src={Image}
width="48"
height="48"
alt="Transaction 04"
/>
</div>
<div className="mt-[31px] text-2xl font-semibold text-emerald-500 mb-1">
0.012 IDN
</div>
<div className="text-sm font-medium text-slate-800 mb-3">
Total amount fee
</div> </div>
<div className="mt-[31px] text-2xl font-semibold text-emerald-500 mb-1">0.012 IDN</div>
<div className="text-sm font-medium text-slate-800 mb-3">Total amount fee</div>
</div> </div>
{/* Divider */} {/* Divider */}
<div className="flex justify-between items-center" aria-hidden="true"> <div
<svg className="w-5 h-5 fill-white" xmlns="http://www.w3.org/2000/svg"> className="flex justify-between items-center"
aria-hidden="true"
>
<svg
className="w-5 h-5 fill-white"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M0 20c5.523 0 10-4.477 10-10S5.523 0 0 0h20v20H0Z" /> <path d="M0 20c5.523 0 10-4.477 10-10S5.523 0 0 0h20v20H0Z" />
</svg> </svg>
<div className="grow w-full h-5 bg-white flex flex-col justify-center"> <div className="grow w-full h-5 bg-white flex flex-col justify-center">
<div className="h-px w-full border-t border-dashed border-slate-200" /> <div className="h-px w-full border-t border-dashed border-slate-200" />
</div> </div>
<svg className="w-5 h-5 fill-white rotate-180" xmlns="http://www.w3.org/2000/svg"> <svg
className="w-5 h-5 fill-white rotate-180"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M0 20c5.523 0 10-4.477 10-10S5.523 0 0 0h20v20H0Z" /> <path d="M0 20c5.523 0 10-4.477 10-10S5.523 0 0 0h20v20H0Z" />
</svg> </svg>
</div> </div>
@@ -463,15 +756,21 @@ const Profile = observer (() => {
<div className="bg-white rounded-b-xl p-5 pt-[22px] pb-10 text-sm space-y-3"> <div className="bg-white rounded-b-xl p-5 pt-[22px] pb-10 text-sm space-y-3">
<div className="flex justify-between space-x-1"> <div className="flex justify-between space-x-1">
<span className="italic">Validator:</span> <span className="italic">Validator:</span>
<span className="font-medium text-slate-700 text-right">IT17 2207 1010 0504 0006 88</span> <span className="font-medium text-slate-700 text-right">
IT17 2207 1010 0504 0006 88
</span>
</div> </div>
<div className="flex justify-between space-x-1"> <div className="flex justify-between space-x-1">
<span className="italic">Recipient:</span> <span className="italic">Recipient:</span>
<span className="font-medium text-slate-700 text-right">IT17 2207 1010 0504 0006 88</span> <span className="font-medium text-slate-700 text-right">
IT17 2207 1010 0504 0006 88
</span>
</div> </div>
<div className="flex justify-between space-x-1"> <div className="flex justify-between space-x-1">
<span className="italic">Transaction:</span> <span className="italic">Transaction:</span>
<span className="font-medium text-slate-700 text-right">145 bytes</span> <span className="font-medium text-slate-700 text-right">
145 bytes
</span>
</div> </div>
</div> </div>
</div> </div>
@@ -483,7 +782,10 @@ const Profile = observer (() => {
className="btn w-full bg-indigo-500 hover:bg-indigo-600 text-white px-[100px] justify-start" className="btn w-full bg-indigo-500 hover:bg-indigo-600 text-white px-[100px] justify-start"
onClick={openRemovePanel} onClick={openRemovePanel}
> >
<svg className="w-4 h-4 fill-current shrink-0" viewBox="0 0 16 16"> <svg
className="w-4 h-4 fill-current shrink-0"
viewBox="0 0 16 16"
>
<path d="m2.457 8.516.969-.99 2.516 2.481 5.324-5.304.985.989-6.309 6.284z" /> <path d="m2.457 8.516.969-.99 2.516 2.481 5.324-5.304.985.989-6.309 6.284z" />
</svg> </svg>
<span className="ml-2">Remove</span> <span className="ml-2">Remove</span>
@@ -492,13 +794,22 @@ const Profile = observer (() => {
className="btn w-full border-slate-200 hover:border-slate-300 text-slate-600 px-[100px] justify-start" className="btn w-full border-slate-200 hover:border-slate-300 text-slate-600 px-[100px] justify-start"
onClick={openUpdatePanel} onClick={openUpdatePanel}
> >
<svg className="w-4 h-4 fill-rose-500 shrink-0" viewBox="0 0 16 16"> <svg
className="w-4 h-4 fill-rose-500 shrink-0"
viewBox="0 0 16 16"
>
<path d="M14.682 2.318A4.485 4.485 0 0 0 11.5 1 4.377 4.377 0 0 0 8 2.707 4.383 4.383 0 0 0 4.5 1a4.5 4.5 0 0 0-3.182 7.682L8 15l6.682-6.318a4.5 4.5 0 0 0 0-6.364Zm-1.4 4.933L8 12.247l-5.285-5A2.5 2.5 0 0 1 4.5 3c1.437 0 2.312.681 3.5 2.625C9.187 3.681 10.062 3 11.5 3a2.5 2.5 0 0 1 1.785 4.251h-.003Z" /> <path d="M14.682 2.318A4.485 4.485 0 0 0 11.5 1 4.377 4.377 0 0 0 8 2.707 4.383 4.383 0 0 0 4.5 1a4.5 4.5 0 0 0-3.182 7.682L8 15l6.682-6.318a4.5 4.5 0 0 0 0-6.364Zm-1.4 4.933L8 12.247l-5.285-5A2.5 2.5 0 0 1 4.5 3c1.437 0 2.312.681 3.5 2.625C9.187 3.681 10.062 3 11.5 3a2.5 2.5 0 0 1 1.785 4.251h-.003Z" />
</svg> </svg>
<span className="ml-2">Update</span> <span className="ml-2">Update</span>
</button> </button>
<button className="btn w-full border-slate-200 hover:border-slate-300 text-slate-600 px-[100px] justify-start"> <button
<svg className="w-4 h-4 fill-rose-500 shrink-0" viewBox="0 0 16 16"> className="btn w-full border-slate-200 hover:border-slate-300 text-slate-600 px-[100px] justify-start"
onClick={openSharePanel}
>
<svg
className="w-4 h-4 fill-rose-500 shrink-0"
viewBox="0 0 16 16"
>
<path d="M14.682 2.318A4.485 4.485 0 0 0 11.5 1 4.377 4.377 0 0 0 8 2.707 4.383 4.383 0 0 0 4.5 1a4.5 4.5 0 0 0-3.182 7.682L8 15l6.682-6.318a4.5 4.5 0 0 0 0-6.364Zm-1.4 4.933L8 12.247l-5.285-5A2.5 2.5 0 0 1 4.5 3c1.437 0 2.312.681 3.5 2.625C9.187 3.681 10.062 3 11.5 3a2.5 2.5 0 0 1 1.785 4.251h-.003Z" /> <path d="M14.682 2.318A4.485 4.485 0 0 0 11.5 1 4.377 4.377 0 0 0 8 2.707 4.383 4.383 0 0 0 4.5 1a4.5 4.5 0 0 0-3.182 7.682L8 15l6.682-6.318a4.5 4.5 0 0 0 0-6.364Zm-1.4 4.933L8 12.247l-5.285-5A2.5 2.5 0 0 1 4.5 3c1.437 0 2.312.681 3.5 2.625C9.187 3.681 10.062 3 11.5 3a2.5 2.5 0 0 1 1.785 4.251h-.003Z" />
</svg> </svg>
<span className="ml-2">Share</span> <span className="ml-2">Share</span>
@@ -511,7 +822,7 @@ const Profile = observer (() => {
</main> </main>
</div> </div>
</div> </div>
) );
}) });
export default Profile export default Profile;

View File

@@ -1,12 +1,12 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import {observer} from "mobx-react-lite"; import { observer } from 'mobx-react-lite';
import Sidebar from '../../partials/Sidebar'; import Sidebar from '../../partials/Sidebar';
import Header from '../../partials/Header'; import Header from '../../partials/Header';
import ValidateTable from "../../partials/validate/ValidateTable"; import ValidateTable from '../../partials/validate/ValidateTable';
import ValidatePanel from "../../partials/validate/ValidatePanel"; import ValidatePanel from '../../partials/validate/ValidatePanel';
import ValidateRoadMap from "../../partials/validationLog/ValidateRoadMap"; import ValidateRoadMap from '../../partials/validationLog/ValidateRoadMap';
import {Link} from "react-router-dom"; import { Link } from 'react-router-dom';
import {registrationStore} from "../../store/store"; import { store } from '../../store/store';
const Validate = observer(() => { const Validate = observer(() => {
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
@@ -26,9 +26,16 @@ const Validate = observer(() => {
{/* Page header */} {/* Page header */}
<div className="pb-2.5"> <div className="pb-2.5">
{/* Title */} {/* Title */}
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">0x28394710234192304719234</h1> <h1 className="text-2xl md:text-3xl text-slate-800 font-bold">
0x28394710234192304719234
</h1>
</div> </div>
<Link className="font-normal text-sm text-indigo-500 hover:text-indigo-600" to="/digitalId/validate">Explore -&gt;</Link> <Link
className="font-normal text-sm text-indigo-500 hover:text-indigo-600"
to="/digitalId/validate"
>
Explore -&gt;
</Link>
<div className="mb-5 mt-5"> <div className="mb-5 mt-5">
<ul className="flex flex-wrap -m-1"> <ul className="flex flex-wrap -m-1">
<li className="m-1"> <li className="m-1">
@@ -53,20 +60,25 @@ const Validate = observer(() => {
</li> </li>
</ul> </ul>
</div> </div>
<ValidateTable setValidatePanelOpen={setValidatePanelOpen}/> <ValidateTable setValidatePanelOpen={setValidatePanelOpen} />
<div className="pb-8 border-b border-zinc-200 mt-[50px]"> <div className="pb-8 border-b border-zinc-200 mt-[50px]">
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">Digital ID validation log </h1> <h1 className="text-2xl md:text-3xl text-slate-800 font-bold">
Digital ID validation log
</h1>
</div> </div>
{registrationStore.transactionsInfo.map(item => { {store.transactionsInfo.map((item) => {
return <ValidateRoadMap season={item} key={item.id}/> return <ValidateRoadMap season={item} key={item.id} />;
})} })}
</div> </div>
</div> </div>
<ValidatePanel validatePanelOpen={validatePanelOpen} setValidatePanelOpen={setValidatePanelOpen}/> <ValidatePanel
validatePanelOpen={validatePanelOpen}
setValidatePanelOpen={setValidatePanelOpen}
/>
</main> </main>
</div> </div>
</div> </div>
) );
}) });
export default Validate; export default Validate;

View File

@@ -1,9 +1,9 @@
import React, {useState} from 'react'; import React, { useState } from 'react';
import ValidateRoadMap from "../../partials/validationLog/ValidateRoadMap"; import ValidateRoadMap from '../../partials/validationLog/ValidateRoadMap';
import {observer} from "mobx-react-lite"; import { observer } from 'mobx-react-lite';
import Sidebar from '../../partials/Sidebar'; import Sidebar from '../../partials/Sidebar';
import Header from '../../partials/Header'; import Header from '../../partials/Header';
import {registrationStore} from "../../store/store"; import { store } from '../../store/store';
const ValidationLog = observer(() => { const ValidationLog = observer(() => {
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
@@ -21,14 +21,16 @@ const ValidationLog = observer(() => {
{/* Page header */} {/* Page header */}
<div className="pb-8 border-b border-zinc-200"> <div className="pb-8 border-b border-zinc-200">
{/* Title */} {/* Title */}
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">Digital ID transaction log </h1> <h1 className="text-2xl md:text-3xl text-slate-800 font-bold">
Digital ID transaction log
</h1>
</div> </div>
{/*Psosts*/} {/*Psosts*/}
<div className="max-w-3xl m-auto mt-6"> <div className="max-w-3xl m-auto mt-6">
<div className="xl:-translate-x-16 max-w-fit"> <div className="xl:-translate-x-16 max-w-fit">
{/* PostsID */} {/* PostsID */}
{registrationStore.transactionsInfo.map(item => { {store.transactionsInfo.map((item) => {
return <ValidateRoadMap season={item} key={item.id}/> return <ValidateRoadMap season={item} key={item.id} />;
})} })}
</div> </div>
</div> </div>
@@ -36,7 +38,7 @@ const ValidationLog = observer(() => {
</main> </main>
</div> </div>
</div> </div>
) );
}) });
export default ValidationLog; export default ValidationLog;

View File

@@ -1,13 +1,17 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import Sidebar from '../../partials/Sidebar'; import Sidebar from '../../partials/Sidebar';
import Header from '../../partials/Header'; import Header from '../../partials/Header';
import {registrationStore} from "../../store/store"; import { store } from '../../store/store';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
const Verify = observer(() => { const Verify = observer(() => {
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
const [descriptionOpen, setDescriptionOpen] = useState(false); const [descriptionOpen, setDescriptionOpen] = useState(false);
const openShareLink = (id) => {
window.open(`${window.location.origin}/#/shared-data/${id}`, '_blank');
};
return ( return (
<div className="flex h-screen overflow-hidden"> <div className="flex h-screen overflow-hidden">
{/* Sidebar */} {/* Sidebar */}
@@ -42,26 +46,40 @@ const Verify = observer(() => {
</div> </div>
{/* Block */} {/* Block */}
<div className="flex flex-col gap-y-4"> <div className="flex flex-col gap-y-4">
{registrationStore.sharedData.map((item, index) => ( {store.sharedData.map((item, index) => (
<div key={index} className="w-[1095px] pl-5 pr-[13px] pb-8 bg-white border border-slate-200 rounded shadow-[0_4px_6px_-1px_rgba(5,23,42,0.08)]"> <div
key={index}
className="w-[1095px] pl-5 pr-[13px] pb-8 bg-white border border-slate-200 rounded shadow-[0_4px_6px_-1px_rgba(5,23,42,0.08)]"
>
{/* Header */} {/* Header */}
<div className="flex justify-end items-center h-[42px]"> <div className="flex justify-end items-center h-[42px]">
{/* Buttons */} {/* Buttons */}
<div className="flex gap-x-[6px]"> <div className="flex gap-x-[6px]">
<div className="flex items-center"> <div className="flex items-center">
<button className="text-slate-400 hover:text-slate-500 rounded-full"> <button
className="text-slate-400 hover:text-slate-500 rounded-full"
onClick={() => openShareLink(item.id)}
>
<span className="sr-only">Link</span> <span className="sr-only">Link</span>
<svg className="w-4 h-4 fill-current shrink-0 text-slate-400" viewBox="0 0 16 16"> <svg
className="w-4 h-4 fill-current shrink-0 text-slate-400"
viewBox="0 0 16 16"
>
<path d="M11 0c1.3 0 2.6.5 3.5 1.5 1 .9 1.5 2.2 1.5 3.5 0 1.3-.5 2.6-1.4 3.5l-1.2 1.2c-.2.2-.5.3-.7.3-.2 0-.5-.1-.7-.3-.4-.4-.4-1 0-1.4l1.1-1.2c.6-.5.9-1.3.9-2.1s-.3-1.6-.9-2.2C12 1.7 10 1.7 8.9 2.8L7.7 4c-.4.4-1 .4-1.4 0-.4-.4-.4-1 0-1.4l1.2-1.1C8.4.5 9.7 0 11 0ZM8.3 12c.4-.4 1-.5 1.4-.1.4.4.4 1 0 1.4l-1.2 1.2C7.6 15.5 6.3 16 5 16c-1.3 0-2.6-.5-3.5-1.5C.5 13.6 0 12.3 0 11c0-1.3.5-2.6 1.5-3.5l1.1-1.2c.4-.4 1-.4 1.4 0 .4.4.4 1 0 1.4L2.9 8.9c-.6.5-.9 1.3-.9 2.1s.3 1.6.9 2.2c1.1 1.1 3.1 1.1 4.2 0L8.3 12Zm1.1-6.8c.4-.4 1-.4 1.4 0 .4.4.4 1 0 1.4l-4.2 4.2c-.2.2-.5.3-.7.3-.2 0-.5-.1-.7-.3-.4-.4-.4-1 0-1.4l4.2-4.2Z" /> <path d="M11 0c1.3 0 2.6.5 3.5 1.5 1 .9 1.5 2.2 1.5 3.5 0 1.3-.5 2.6-1.4 3.5l-1.2 1.2c-.2.2-.5.3-.7.3-.2 0-.5-.1-.7-.3-.4-.4-.4-1 0-1.4l1.1-1.2c.6-.5.9-1.3.9-2.1s-.3-1.6-.9-2.2C12 1.7 10 1.7 8.9 2.8L7.7 4c-.4.4-1 .4-1.4 0-.4-.4-.4-1 0-1.4l1.2-1.1C8.4.5 9.7 0 11 0ZM8.3 12c.4-.4 1-.5 1.4-.1.4.4.4 1 0 1.4l-1.2 1.2C7.6 15.5 6.3 16 5 16c-1.3 0-2.6-.5-3.5-1.5C.5 13.6 0 12.3 0 11c0-1.3.5-2.6 1.5-3.5l1.1-1.2c.4-.4 1-.4 1.4 0 .4.4.4 1 0 1.4L2.9 8.9c-.6.5-.9 1.3-.9 2.1s.3 1.6.9 2.2c1.1 1.1 3.1 1.1 4.2 0L8.3 12Zm1.1-6.8c.4-.4 1-.4 1.4 0 .4.4.4 1 0 1.4l-4.2 4.2c-.2.2-.5.3-.7.3-.2 0-.5-.1-.7-.3-.4-.4-.4-1 0-1.4l4.2-4.2Z" />
</svg> </svg>
</button> </button>
</div> </div>
<div className="flex items-center"> <div className="flex items-center">
<button <button
className={`text-slate-400 hover:text-slate-500 transform ${descriptionOpen && 'rotate-180'}`} className={`text-slate-400 hover:text-slate-500 transform ${
descriptionOpen && 'rotate-180'
}`}
aria-expanded={descriptionOpen} aria-expanded={descriptionOpen}
> >
<svg className="w-8 h-8 fill-current" viewBox="0 0 32 32"> <svg
className="w-8 h-8 fill-current"
viewBox="0 0 32 32"
>
<path d="M16 20l-5.4-5.4 1.4-1.4 4 4 4-4 1.4 1.4z" /> <path d="M16 20l-5.4-5.4 1.4-1.4 4 4 4-4 1.4 1.4z" />
</svg> </svg>
</button> </button>
@@ -69,13 +87,17 @@ const Verify = observer(() => {
</div> </div>
</div> </div>
{/* Content */} {/* Content */}
<div className='flex flex-col gap-y-[9px]'> <div className="flex flex-col gap-y-[9px]">
{item.data.map((elem, index) => ( {item.data.map((elem, index) => (
<div key={index} className='flex gap-y-2'> <div key={index} className="flex gap-y-2">
<div className='flex flex-col'> <div className="flex flex-col">
<div className='flex flex-row items-center'> <div className="flex flex-row items-center">
<span className='font-inter font-semibold text-slate-800 text-base w-[204px]'>{elem.label}</span> <span className="font-inter font-semibold text-slate-800 text-base w-[204px]">
<span className='font-inter truncate font-semibold text-descriptionSize text-slate-600 underline w-[770px]'>{elem.value}</span> {elem.label}
</span>
<span className="font-inter truncate font-semibold text-descriptionSize text-slate-600 underline w-[770px]">
{elem.value}
</span>
</div> </div>
</div> </div>
</div> </div>
@@ -88,7 +110,7 @@ const Verify = observer(() => {
</main> </main>
</div> </div>
</div> </div>
) );
}) });
export default Verify; export default Verify;

View File

@@ -0,0 +1,119 @@
import React, { useEffect, useMemo, useState } from 'react';
import Sidebar from '../../partials/Sidebar';
import Header from '../../partials/Header';
import DropdownTransaction from '../../components/DropdownTransaction';
import TransactionsTable from '../../partials/finance/TransactionsTable';
import PaginationClassic from '../../components/PaginationClassic';
import { observer } from 'mobx-react-lite';
import TransactionPanel from '../../partials/finance/TransactionPanel';
import { Filters } from '../../components/Filters';
const filters = {
'View All': 'all',
Voted: 'Unvote',
Pending: 'Pending',
};
const Delegates = observer(() => {
const [sidebarOpen, setSidebarOpen] = useState(false);
const [currentDelegate, setCurrentDelegate] = useState(null);
const [currentFilter, setCurrentFilter] = useState('View All');
const [lockedFor, setLockedFor] = useState('Voiting');
const delegates = useMemo(() => {
if (filters[currentFilter] === 'all') return store.delegates;
return store.delegates.filter((e) => e.status === filters[currentFilter]);
}, [store.delegates, currentFilter]);
const lockedLabel = (
lockedFor === 'Voiting'
? Object.values(store.accountSentVotes)
: Object.values(store.accountLockedVotesCanReturn)
.flat()
.map((e) => e.amount)
)
.reduce((sum, e) => sum + e, 0n)
.toString();
useEffect(() => {
store.fetchDelegates(0);
}, []);
return (
<div className="flex h-screen overflow-hidden">
{/* Sidebar */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* Content area */}
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden bg-white">
{/* Site header */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<main className="overflow-auto">
{/* Content */}
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
{/* Page header */}
<div className="sm:flex sm:justify-between sm:items-center mb-4 md:mb-2">
{/* Left: Title */}
<div className="mb-4 sm:mb-0">
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">
{lockedLabel}
/idn
</h1>
</div>
</div>
<div className="mb-5">
<span>Locked for </span>
<DropdownTransaction value={lockedFor} onChange={setLockedFor} />
</div>
{/* Filters */}
<Filters
value={currentFilter}
values={Object.keys(filters)}
onChange={setCurrentFilter}
/>
{/* Table */}
<TransactionsTable
data={delegates}
lockedFor={lockedFor}
rowClick={(delegate) =>
(lockedFor === 'Voiting' ||
store.accountLockedVotesCanReturnSum[delegate.address]) &&
setCurrentDelegate(delegate)
}
/>
{/*/!* Pagination *!/*/}
{/*<div className="mt-8">*/}
{/* <PaginationClassic*/}
{/* count={store.delegatesMeta.count}*/}
{/* limit={store.delegatesMeta.limit}*/}
{/* offset={store.delegatesMeta.offset}*/}
{/* onNextPage={() => setFetchOffset((prev) => prev + 10)}*/}
{/* onPrevPage={() => setFetchOffset((prev) => prev - 10)}*/}
{/* />*/}
{/*</div>*/}
<TransactionPanel
transactionPanelOpen={!!currentDelegate}
onClose={() => setCurrentDelegate(null)}
delegate={currentDelegate || {}}
unlocking={lockedFor !== 'Voiting'}
postTransaction={(address, amount) => {
if (lockedFor === 'Voiting') store.pushVoteTransaction(address, amount);
else store.pushUnlockTransaction(address);
}}
/>
</div>
</main>
</div>
</div>
);
});
export default Delegates;

View File

@@ -0,0 +1,105 @@
import React, { useEffect, useState } from 'react';
import Sidebar from '../../partials/Sidebar';
import Header from '../../partials/Header';
import VPNServiceImg from '../../images/vpn_service.png';
import { observer } from 'mobx-react-lite';
import { useNavigate } from 'react-router-dom';
const VPNPrepare = observer(() => {
const [sidebarOpen, setSidebarOpen] = useState(false);
useEffect(() => {
store.fetchVPNServers();
}, []);
const navigate = useNavigate();
const canVote = store.accountBalance > 20;
const clickButton = () => {
if (canVote) navigate('/services/delegates');
else window.open('https://testnet.idntty.org/faucet', '_blank');
};
useEffect(() => {
if (store.canVPN) navigate('/services/vpn/servers');
}, [store.canVPN]);
if (store.loading && !store.canVPN) return null;
return (
<div className="flex h-screen overflow-hidden">
{/* Sidebar */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* Content area */}
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden bg-white">
{/* Site header */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<main>
<div className="lg:relative lg:flex border-b border-slate-200">
{/* Content */}
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl mx-auto">
{/* Page header */}
<div className="sm:flex sm:justify-between sm:items-center">
{/* Left: Title */}
<div className="mb-4 sm:mb-0">
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">
VPN Service
</h1>
</div>
</div>
</div>
</div>
<div className="flex justify-center py-16">
<div className="flex w-2/4 flex-col gap-2 justify-center items-center">
<img
width="64"
height="64"
src={VPNServiceImg}
alt="VPN service"
/>
<h2 className="text-xl md:text-2xl text-slate-800 font-bold text-center">
{canVote
? 'Vote for VPN delegate to get service'
: 'You cannot get the VPN service, sorry 😢'}
</h2>
<p className="text-gray-600 text-center">
{canVote ? (
'The tokens used for voting are locked, which means that they still belong to you but they cannot be used for transactions. The funds are locked for as long as you are voting using these tokens and you are able to unlock them any time you want, by removing them from the votes.'
) : (
<>
Only users who can vote for a VPN delegate using{' '}
<b>at least 19.9 tokens</b> can use this service.
Unfortunately, your account currently has{' '}
<b>only {store.accountBalance} tokens</b>, which is not
enough to vote.
</>
)}
</p>
<button
className="btn bg-indigo-500 hover:bg-indigo-600 text-white max-w-fit"
onClick={clickButton}
>
<svg
className="w-4 h-4 fill-current opacity-50 shrink-0"
viewBox="0 0 16 16"
>
<path d="M15 7H9V1c0-.6-.4-1-1-1S7 .4 7 1v6H1c-.6 0-1 .4-1 1s.4 1 1 1h6v6c0 .6.4 1 1 1s1-.4 1-1V9h6c.6 0 1-.4 1-1s-.4-1-1-1z" />
</svg>
<span className="hidden xs:block mx-6">
{canVote ? 'Vote' : 'Get It'}
</span>
</button>
</div>
</div>
</main>
</div>
</div>
);
});
export default VPNPrepare;

View File

@@ -0,0 +1,268 @@
import React, { useEffect, useMemo, useState } from 'react';
import Sidebar from '../../partials/Sidebar';
import Header from '../../partials/Header';
import { QRCodeSVG } from 'qrcode.react';
import VPNServeIcon from '../../images/vpn_server_icon.png';
import { Filters } from '../../components/Filters';
import { observer } from 'mobx-react-lite';
import ReactCountryFlag from 'react-country-flag';
import Transition from '../../utils/Transition';
const filters = {
'View All': 'all',
Active: true,
Offline: false,
};
const VPNServers = observer(() => {
const [sidebarOpen, setSidebarOpen] = useState(false);
const [selectedServer, setSelectedServer] = useState(0);
const [currentFilter, setCurrentFilter] = useState('View All');
const servers = useMemo(() => {
if (filters[currentFilter] === 'all') return store.vpnServers;
return store.vpnServers.filter((e) => e.state === filters[currentFilter]);
}, [store.vpnServers, currentFilter]);
const qrContent = useMemo(
() => `
[Interface]
PrivateKey = ${store.vpnPrivateKey}
Address = ${servers?.[selectedServer]?.address}
DNS = ${servers?.[selectedServer]?.dns || '1.1.1.1'}
[Peer]
PublicKey = ${servers?.[selectedServer]?.serverPublickKey}
AllowedIPs = 0.0.0.0/0
Endpoint = ${servers?.[selectedServer]?.endpoint}
PersistentKeepalive = 25
`,
[servers, store.vpnPrivateKey, selectedServer]
);
useEffect(() => {
setSelectedServer(0);
}, [currentFilter]);
const downloadTxtFile = () => {
const element = document.createElement('a');
const file = new Blob([qrContent], { type: 'text/plain' });
element.href = URL.createObjectURL(file);
element.download = 'IDNTTY-' + servers?.[selectedServer]?.country + '.conf';
document.body.appendChild(element); // Required for this to work in FireFox
element.click();
};
useEffect(() => {
store.fetchVPNServers();
}, []);
return (
<div className="flex h-screen overflow-hidden">
{/* Sidebar */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* Content area */}
<div className="relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden bg-white">
{/* Site header */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<main>
<div className="lg:relative lg:flex">
{/* Content */}
<div className="px-4 sm:px-6 lg:px-8 py-8 w-full max-w-9xl">
{/* Page header */}
<div className="sm:flex sm:justify-between sm:items-center mb-5">
{/* Left: Title */}
<div className="mb-4 sm:mb-0">
<h1 className="text-2xl md:text-3xl text-slate-800 font-bold">
Servers
</h1>
</div>
</div>
{/* Filters */}
<Filters
value={currentFilter}
values={Object.keys(filters)}
onChange={setCurrentFilter}
/>
{/* Credit cards */}
<div className="space-y-2">
{servers?.map((e, i) => (
<label
className="relative block cursor-pointer text-left w-full"
onClick={() => setSelectedServer(i)}
key={e.serverPublickKey}
>
<input
type="radio"
name="radio-buttons"
className="peer sr-only"
checked={i === selectedServer}
onChange={() => {}}
/>
<div className="p-4 rounded border border-slate-200 hover:border-slate-300 shadow-sm duration-150 ease-in-out">
<div className="grid grid-cols-12 items-center gap-x-2">
{/* Card */}
<div className="col-span-6 order-1 sm:order-none flex items-center space-x-4 ">
<ReactCountryFlag
countryCode={e.shortCountry}
svg
style={{ width: '36px', height: 'auto' }}
/>
<div>
<div className="text-sm font-medium text-slate-800">
{e.endpoint}
</div>
<div className="text-xs">{e.serverPublickKey}</div>
</div>
</div>
{/* Card limits */}
<div className="col-span-3 order-1 sm:order-none text-right sm:text-center">
<div className="text-sm">
{e.transferSum} / 50.00 GB
</div>
</div>
{/* Card status */}
<div className="col-span-3 order-2 sm:order-none text-right">
{e?.state ? (
<div className="text-xs inline-flex font-medium bg-emerald-100 text-emerald-600 rounded-full text-center px-2.5 py-1">
Active
</div>
) : (
<div className="text-xs inline-flex font-medium bg-amber-100 text-amber-600 rounded-full text-center px-2.5 py-1">
Offline
</div>
)}
</div>
</div>
</div>
<div
className="absolute inset-0 border-2 border-transparent peer-checked:border-indigo-400 rounded pointer-events-none"
aria-hidden="true"
/>
</label>
))}
</div>
</div>
{/* Sidebar */}
<Transition
show={servers?.length > 0 && servers?.[selectedServer]?.state}
enter="transition-transform duration-200 ease-in"
enterStart="translate-x-full"
enterEnd="translate-x-"
leave="transition-transform duration-200 ease-out"
leaveStart="translate-x-"
leaveEnd="translate-x-full"
>
<div className="lg:sticky lg:top-16 bg-slate-50 lg:overflow-x-hidden lg:overflow-y-auto no-scrollbar lg:shrink-0 border-t lg:border-t-0 lg:border-l border-slate-200 lg:w-[390px] lg:h-[calc(100vh-64px)]">
<div className="py-8 px-4 lg:px-8 h-full">
<div className="max-w-sm mx-auto lg:max-w-none flex flex-col justify-between h-full">
<div>
<div className="text-slate-800 font-semibold text-center mb-6">
{servers?.[selectedServer]?.country} -{' '}
{servers?.[selectedServer]?.region}
</div>
{/* Credit Card */}
<div className="flex justify-center">
<QRCodeSVG
value={qrContent}
size={200}
includeMargin
level="L"
/>
</div>
{/* Details */}
<div className="mt-6">
<div className="text-sm font-semibold text-slate-800 mb-1">
Details
</div>
<ul>
<li className="flex items-center justify-between py-3 border-b border-slate-200">
<div className="text-sm">Server address</div>
<div className="text-sm font-medium text-slate-800 ml-2">
{servers?.[selectedServer]?.endpoint}
</div>
</li>
<li className="flex items-center justify-between py-3 border-b border-slate-200">
<div className="text-sm">Status</div>
<div className="flex items-center whitespace-nowrap">
{servers?.[selectedServer]?.state ? (
<>
<div className="w-2 h-2 rounded-full bg-emerald-500 mr-2" />
<div className="text-sm font-medium text-slate-800">
Active
</div>
</>
) : (
<>
<div className="w-2 h-2 rounded-full mr-2 bg-amber-500" />
<div className="text-sm font-medium text-slate-800">
Offline
</div>
</>
)}
</div>
</li>
</ul>
</div>
{/* Payment Limits */}
<div className="mt-6">
<div className="text-sm font-semibold text-slate-800 mb-4">
Transfer
</div>
<div className="pb-4 border-b border-slate-200">
<div className="flex justify-between text-sm mb-2">
<div>Spent This Month</div>
<div className="italic">
{servers?.[selectedServer]?.transferSum}{' '}
<span className="text-slate-400">/</span> 50.00 GB
</div>
</div>
<div className="relative w-full h-2 bg-slate-300">
<div
className="absolute inset-0 bg-indigo-500"
aria-hidden="true"
style={{
width: `${servers?.[selectedServer]?.trafficUsed}%`,
}}
/>
</div>
</div>
</div>
</div>
<button
className="btn w-full border-slate-200 hover:border-slate-300 text-slate-600"
onClick={downloadTxtFile}
>
<svg
className="w-4 h-4 fill-current text-slate-400 shrink-0 rotate-180"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M8 4c-.3 0-.5.1-.7.3L1.6 10 3 11.4l4-4V16h2V7.4l4 4 1.4-1.4-5.7-5.7C8.5 4.1 8.3 4 8 4ZM1 2h14V0H1v2Z" />
</svg>
<span className="ml-2">Download</span>
</button>
</div>
</div>
</div>
</Transition>
</div>
</main>
</div>
</div>
);
});
export default VPNServers;

View File

@@ -1,25 +1,16 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import SearchModal from '../components/ModalSearch';
import Notifications from '../components/DropdownNotifications';
import Help from '../components/DropdownHelp';
import UserMenu from '../components/DropdownProfile'; import UserMenu from '../components/DropdownProfile';
function Header({ function Header({ sidebarOpen, setSidebarOpen }) {
sidebarOpen, const [searchModalOpen, setSearchModalOpen] = useState(false);
setSidebarOpen
}) {
const [searchModalOpen, setSearchModalOpen] = useState(false)
return ( return (
<header className="sticky top-0 bg-white border-b border-slate-200 z-30"> <header className="sticky top-0 bg-white border-b border-slate-200 z-30">
<div className="px-4 sm:px-6 lg:px-8"> <div className="px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16 -mb-px"> <div className="flex items-center justify-between h-16 -mb-px">
{/* Header: Left side */} {/* Header: Left side */}
<div className="flex"> <div className="flex">
{/* Hamburger button */} {/* Hamburger button */}
<button <button
className="text-slate-500 hover:text-slate-600 lg:hidden" className="text-slate-500 hover:text-slate-600 lg:hidden"
@@ -28,37 +19,22 @@ function Header({
onClick={() => setSidebarOpen(!sidebarOpen)} onClick={() => setSidebarOpen(!sidebarOpen)}
> >
<span className="sr-only">Open sidebar</span> <span className="sr-only">Open sidebar</span>
<svg className="w-6 h-6 fill-current" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg
className="w-6 h-6 fill-current"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<rect x="4" y="5" width="16" height="2" /> <rect x="4" y="5" width="16" height="2" />
<rect x="4" y="11" width="16" height="2" /> <rect x="4" y="11" width="16" height="2" />
<rect x="4" y="17" width="16" height="2" /> <rect x="4" y="17" width="16" height="2" />
</svg> </svg>
</button> </button>
</div> </div>
{/* Header: Right side */} {/* Header: Right side */}
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<button
className={`w-8 h-8 flex items-center justify-center bg-slate-100 hover:bg-slate-200 transition duration-150 rounded-full ml-3 ${searchModalOpen && 'bg-slate-200'}`}
onClick={(e) => { e.stopPropagation(); setSearchModalOpen(true); }}
aria-controls="search-modal"
>
<span className="sr-only">Search</span>
<svg className="w-4 h-4" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path className="fill-current text-slate-500" d="M7 14c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7zM7 2C4.243 2 2 4.243 2 7s2.243 5 5 5 5-2.243 5-5-2.243-5-5-5z" />
<path className="fill-current text-slate-400" d="M15.707 14.293L13.314 11.9a8.019 8.019 0 01-1.414 1.414l2.393 2.393a.997.997 0 001.414 0 .999.999 0 000-1.414z" />
</svg>
</button>
<SearchModal id="search-modal" searchId="search" modalOpen={searchModalOpen} setModalOpen={setSearchModalOpen} />
<Notifications align="right" />
<Help align="right" />
{/* Divider */}
<hr className="w-px h-6 bg-slate-200 mx-3" />
<UserMenu align="right" /> <UserMenu align="right" />
</div> </div>
</div> </div>
</div> </div>
</header> </header>

View File

@@ -4,24 +4,27 @@ import Logo from '../images/logo.png';
import SidebarLinkGroup from './SidebarLinkGroup'; import SidebarLinkGroup from './SidebarLinkGroup';
function Sidebar({ function Sidebar({ sidebarOpen, setSidebarOpen }) {
sidebarOpen,
setSidebarOpen
}) {
const location = useLocation(); const location = useLocation();
const { pathname } = location; const { pathname } = location;
const trigger = useRef(null); const trigger = useRef(null);
const sidebar = useRef(null); const sidebar = useRef(null);
const storedSidebarExpanded = localStorage.getItem('sidebar-expanded'); const storedSidebarExpanded = localStorage.getItem('sidebar-expanded');
const [sidebarExpanded, setSidebarExpanded] = useState(storedSidebarExpanded === null ? false : storedSidebarExpanded === 'true'); const [sidebarExpanded, setSidebarExpanded] = useState(
storedSidebarExpanded === null ? false : storedSidebarExpanded === 'true'
);
// close on click outside // close on click outside
useEffect(() => { useEffect(() => {
const clickHandler = ({ target }) => { const clickHandler = ({ target }) => {
if (!sidebar.current || !trigger.current) return; if (!sidebar.current || !trigger.current) return;
if (!sidebarOpen || sidebar.current.contains(target) || trigger.current.contains(target)) return; if (
!sidebarOpen ||
sidebar.current.contains(target) ||
trigger.current.contains(target)
)
return;
setSidebarOpen(false); setSidebarOpen(false);
}; };
document.addEventListener('click', clickHandler); document.addEventListener('click', clickHandler);
@@ -76,13 +79,17 @@ function Sidebar({
aria-expanded={sidebarOpen} aria-expanded={sidebarOpen}
> >
<span className="sr-only">Close sidebar</span> <span className="sr-only">Close sidebar</span>
<svg className="w-6 h-6 fill-current" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg
className="w-6 h-6 fill-current"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M10.7 18.7l1.4-1.4L7.8 13H20v-2H7.8l4.3-4.3-1.4-1.4L4 12z" /> <path d="M10.7 18.7l1.4-1.4L7.8 13H20v-2H7.8l4.3-4.3-1.4-1.4L4 12z" />
</svg> </svg>
</button> </button>
{/* Logo */} {/* Logo */}
<NavLink end to="/dashboard" className="block"> <NavLink end to="/digitalId/profile-id" className="block">
<img alt='logo' src={Logo} width="89" height="32"/> <img alt="logo" src={Logo} width="89" height="32" />
</NavLink> </NavLink>
</div> </div>
@@ -91,139 +98,68 @@ function Sidebar({
{/* Pages group */} {/* Pages group */}
<div> <div>
<h3 className="text-xs uppercase text-slate-500 font-semibold pl-3"> <h3 className="text-xs uppercase text-slate-500 font-semibold pl-3">
<span className="hidden lg:block lg:sidebar-expanded:hidden 2xl:hidden text-center w-6" aria-hidden="true"> <span
className="hidden lg:block lg:sidebar-expanded:hidden 2xl:hidden text-center w-6"
aria-hidden="true"
>
</span> </span>
<span className="lg:hidden lg:sidebar-expanded:block 2xl:block">Pages</span> <span className="lg:hidden lg:sidebar-expanded:block 2xl:block">
Pages
</span>
</h3> </h3>
<ul className="mt-3"> <ul className="mt-3">
{/* Dashboard */}
<SidebarLinkGroup activecondition={pathname === '/' || pathname.includes('dashboard')}>
{(handleClick, open) => {
return (
<React.Fragment>
<a
href="#0"
className={`block text-slate-200 hover:text-white truncate transition duration-150 ${
(pathname === '/' || pathname.includes('dashboard')) && 'hover:text-slate-200'
}`}
onClick={(e) => {
e.preventDefault();
sidebarExpanded ? handleClick() : setSidebarExpanded(true);
}}
>
<div className="flex items-center justify-between">
<div className="flex items-center">
<svg className="shrink-0 h-6 w-6" viewBox="0 0 24 24">
<path
className={`fill-current text-slate-400 ${
(pathname === '/' || pathname.includes('dashboard')) && '!text-indigo-500'
}`}
d="M12 0C5.383 0 0 5.383 0 12s5.383 12 12 12 12-5.383 12-12S18.617 0 12 0z"
/>
<path
className={`fill-current text-slate-600 ${(pathname === '/' || pathname.includes('dashboard')) && 'text-indigo-600'}`}
d="M12 3c-4.963 0-9 4.037-9 9s4.037 9 9 9 9-4.037 9-9-4.037-9-9-9z"
/>
<path
className={`fill-current text-slate-400 ${(pathname === '/' || pathname.includes('dashboard')) && 'text-indigo-200'}`}
d="M12 15c-1.654 0-3-1.346-3-3 0-.462.113-.894.3-1.285L6 6l4.714 3.301A2.973 2.973 0 0112 9c1.654 0 3 1.346 3 3s-1.346 3-3 3z"
/>
</svg>
<span className="text-sm font-medium ml-3 lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Dashboard
</span>
</div>
{/* Icon */}
<div className="flex shrink-0 ml-2">
<svg
className={`w-3 h-3 shrink-0 ml-1 fill-current text-slate-400 ${open && 'transform rotate-180'}`}
viewBox="0 0 12 12"
>
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
</svg>
</div>
</div>
</a>
<div className="lg:hidden lg:sidebar-expanded:block 2xl:block">
<ul className={`pl-9 mt-1 ${!open && 'hidden'}`}>
<li className="mb-1 last:mb-0">
<NavLink
end
to="/dashboard"
className={({ isActive }) =>
'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' + (isActive ? '!text-indigo-500' : '')
}
>
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Main
</span>
</NavLink>
</li>
<li className="mb-1 last:mb-0">
<NavLink
end
to="/dashboard/analytics"
className={({ isActive }) =>
'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' + (isActive ? '!text-indigo-500' : '')
}
>
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Analytics
</span>
</NavLink>
</li>
<li className="mb-1 last:mb-0">
<NavLink
end
to="/dashboard/fintech"
className={({ isActive }) =>
'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' + (isActive ? '!text-indigo-500' : '')
}
>
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Fintech
</span>
</NavLink>
</li>
</ul>
</div>
</React.Fragment>
);
}}
</SidebarLinkGroup>
{/* digitalId */} {/* digitalId */}
<SidebarLinkGroup activecondition={pathname.includes('digitalId')}> <SidebarLinkGroup
activecondition={pathname.includes('digitalId')}
>
{(handleClick, open) => { {(handleClick, open) => {
return ( return (
<React.Fragment> <React.Fragment>
<a <a
href="#0"
className={`block text-slate-200 hover:text-white truncate transition duration-150 ${ className={`block text-slate-200 hover:text-white truncate transition duration-150 ${
pathname.includes('digitalId') && 'hover:text-slate-200' pathname.includes('digitalId') &&
'hover:text-slate-200'
}`} }`}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
sidebarExpanded ? handleClick() : setSidebarExpanded(true); sidebarExpanded
? handleClick()
: setSidebarExpanded(true);
}} }}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center"> <div className="flex items-center">
<svg className="shrink-0 h-6 w-6" viewBox="0 0 24 24"> <svg
className="shrink-0 h-6 w-6"
viewBox="0 0 24 24"
>
<path <path
className={`fill-current text-slate-600 ${pathname.includes('digitalId') && 'text-indigo-500'}`} className={`fill-current text-slate-600 ${
pathname.includes('digitalId') &&
'text-indigo-500'
}`}
d="M19.714 14.7l-7.007 7.007-1.414-1.414 7.007-7.007c-.195-.4-.298-.84-.3-1.286a3 3 0 113 3 2.969 2.969 0 01-1.286-.3z" d="M19.714 14.7l-7.007 7.007-1.414-1.414 7.007-7.007c-.195-.4-.298-.84-.3-1.286a3 3 0 113 3 2.969 2.969 0 01-1.286-.3z"
/> />
<path <path
className={`fill-current text-slate-400 ${pathname.includes('digitalId') && 'text-indigo-300'}`} className={`fill-current text-slate-400 ${
pathname.includes('digitalId') &&
'text-indigo-300'
}`}
d="M10.714 18.3c.4-.195.84-.298 1.286-.3a3 3 0 11-3 3c.002-.446.105-.885.3-1.286l-6.007-6.007 1.414-1.414 6.007 6.007z" d="M10.714 18.3c.4-.195.84-.298 1.286-.3a3 3 0 11-3 3c.002-.446.105-.885.3-1.286l-6.007-6.007 1.414-1.414 6.007 6.007z"
/> />
<path <path
className={`fill-current text-slate-600 ${pathname.includes('digitalId') && 'text-indigo-500'}`} className={`fill-current text-slate-600 ${
pathname.includes('digitalId') &&
'text-indigo-500'
}`}
d="M5.7 10.714c.195.4.298.84.3 1.286a3 3 0 11-3-3c.446.002.885.105 1.286.3l7.007-7.007 1.414 1.414L5.7 10.714z" d="M5.7 10.714c.195.4.298.84.3 1.286a3 3 0 11-3-3c.446.002.885.105 1.286.3l7.007-7.007 1.414 1.414L5.7 10.714z"
/> />
<path <path
className={`fill-current text-slate-400 ${pathname.includes('digitalId') && 'text-indigo-300'}`} className={`fill-current text-slate-400 ${
pathname.includes('digitalId') &&
'text-indigo-300'
}`}
d="M19.707 9.292a3.012 3.012 0 00-1.415 1.415L13.286 5.7c-.4.195-.84.298-1.286.3a3 3 0 113-3 2.969 2.969 0 01-.3 1.286l5.007 5.006z" d="M19.707 9.292a3.012 3.012 0 00-1.415 1.415L13.286 5.7c-.4.195-.84.298-1.286.3a3 3 0 113-3 2.969 2.969 0 01-.3 1.286l5.007 5.006z"
/> />
</svg> </svg>
@@ -234,7 +170,9 @@ function Sidebar({
{/* Icon */} {/* Icon */}
<div className="flex shrink-0 ml-2"> <div className="flex shrink-0 ml-2">
<svg <svg
className={`w-3 h-3 shrink-0 ml-1 fill-current text-slate-400 ${open && 'transform rotate-180'}`} className={`w-3 h-3 shrink-0 ml-1 fill-current text-slate-400 ${
open && 'transform rotate-180'
}`}
viewBox="0 0 12 12" viewBox="0 0 12 12"
> >
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" /> <path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
@@ -249,7 +187,8 @@ function Sidebar({
end end
to="/digitalId/profile-id" to="/digitalId/profile-id"
className={({ isActive }) => className={({ isActive }) =>
'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' + (isActive ? '!text-indigo-500' : '') 'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' +
(isActive ? '!text-indigo-500' : '')
} }
> >
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200"> <span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
@@ -257,25 +196,26 @@ function Sidebar({
</span> </span>
</NavLink> </NavLink>
</li> </li>
<li className="mb-1 last:mb-0"> {/*<li className="mb-1 last:mb-0">*/}
<NavLink {/* <NavLink*/}
end {/* end*/}
to="/digitalId/validate" {/* to="/digitalId/validate"*/}
className={({ isActive }) => {/* className={({ isActive }) =>*/}
'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' + (isActive ? '!text-indigo-500' : '') {/* 'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' + (isActive ? '!text-indigo-500' : '')*/}
} {/* }*/}
> {/* >*/}
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200"> {/* <span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">*/}
Validate {/* Validate*/}
</span> {/* </span>*/}
</NavLink> {/* </NavLink>*/}
</li> {/*</li>*/}
<li className="mb-1 last:mb-0"> <li className="mb-1 last:mb-0">
<NavLink <NavLink
end end
to="/digitalId/validation-log" to="/digitalId/validation-log"
className={({ isActive }) => className={({ isActive }) =>
'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' + (isActive ? '!text-indigo-500' : '') 'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' +
(isActive ? '!text-indigo-500' : '')
} }
> >
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200"> <span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
@@ -288,7 +228,8 @@ function Sidebar({
end end
to="/digitalId/verify" to="/digitalId/verify"
className={({ isActive }) => className={({ isActive }) =>
'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' + (isActive ? '!text-indigo-500' : '') 'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' +
(isActive ? '!text-indigo-500' : '')
} }
> >
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200"> <span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
@@ -302,38 +243,118 @@ function Sidebar({
); );
}} }}
</SidebarLinkGroup> </SidebarLinkGroup>
{/* Messages */} <SidebarLinkGroup activecondition={pathname.includes('services')}>
<li className={`px-3 py-2 rounded-sm mb-0.5 last:mb-0 ${pathname.includes('messages') && 'bg-slate-900'}`}> {(handleClick, open) => {
<NavLink return (
end <React.Fragment>
to="/messages" <a
className={`block text-slate-200 hover:text-white truncate transition duration-150 ${ className={`block text-slate-200 hover:text-white truncate transition duration-150 ${
pathname.includes('messages') && 'hover:text-slate-200' pathname.includes('services') &&
'hover:text-slate-200'
}`} }`}
onClick={(e) => {
e.preventDefault();
sidebarExpanded
? handleClick()
: setSidebarExpanded(true);
}}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="grow flex items-center"> <div className="flex items-center">
<svg className="shrink-0 h-6 w-6" viewBox="0 0 24 24"> <svg
<path className="shrink-0 h-6 w-6"
className={`fill-current text-slate-600 ${pathname.includes('messages') && 'text-indigo-500'}`} viewBox="0 0 24 24"
d="M14.5 7c4.695 0 8.5 3.184 8.5 7.111 0 1.597-.638 3.067-1.7 4.253V23l-4.108-2.148a10 10 0 01-2.692.37c-4.695 0-8.5-3.184-8.5-7.11C6 10.183 9.805 7 14.5 7z" >
<circle
className={`fill-current text-slate-400 ${
pathname.includes('services') &&
'text-indigo-300'
}`}
cx="18.5"
cy="5.5"
r="4.5"
/> />
<path <circle
className={`fill-current text-slate-400 ${pathname.includes('messages') && 'text-indigo-300'}`} className={`fill-current text-slate-600 ${
d="M11 1C5.477 1 1 4.582 1 9c0 1.797.75 3.45 2 4.785V19l4.833-2.416C8.829 16.85 9.892 17 11 17c5.523 0 10-3.582 10-8s-4.477-8-10-8z" pathname.includes('services') &&
'text-indigo-500'
}`}
cx="5.5"
cy="5.5"
r="4.5"
/>
<circle
className={`fill-current text-slate-600 ${
pathname.includes('services') &&
'text-indigo-500'
}`}
cx="18.5"
cy="18.5"
r="4.5"
/>
<circle
className={`fill-current text-slate-400 ${
pathname.includes('services') &&
'text-indigo-300'
}`}
cx="5.5"
cy="18.5"
r="4.5"
/> />
</svg> </svg>
<span className="text-sm font-medium ml-3 lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200"> <span className="text-sm font-medium ml-3 lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Messages Services
</span> </span>
</div> </div>
{/* Badge */} {/* Icon */}
<div className="flex flex-shrink-0 ml-2"> <div className="flex shrink-0 ml-2">
<span className="inline-flex items-center justify-center h-5 text-xs font-medium text-white bg-indigo-500 px-2 rounded">4</span> <svg
className={`w-3 h-3 shrink-0 ml-1 fill-current text-slate-400 ${
open && 'transform rotate-180'
}`}
viewBox="0 0 12 12"
>
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z" />
</svg>
</div> </div>
</div> </div>
</a>
<div className="lg:hidden lg:sidebar-expanded:block 2xl:block">
<ul className={`pl-9 mt-1 ${!open && 'hidden'}`}>
<li className="mb-1 last:mb-0">
<NavLink
end
to="/services/delegates"
className={({ isActive }) =>
'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' +
(isActive ? '!text-indigo-500' : '')
}
>
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Delegates
</span>
</NavLink> </NavLink>
</li> </li>
<li className="mb-1 last:mb-0">
<NavLink
end
to="/services/vpn"
className={({ isActive }) =>
'block text-slate-400 hover:text-slate-200 transition duration-150 truncate ' +
(isActive ? '!text-indigo-500' : '')
}
>
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
VPN
</span>
</NavLink>
</li>
</ul>
</div>
</React.Fragment>
);
}}
</SidebarLinkGroup>
</ul> </ul>
</div> </div>
</div> </div>
@@ -343,8 +364,14 @@ function Sidebar({
<div className="px-3 py-2"> <div className="px-3 py-2">
<button onClick={() => setSidebarExpanded(!sidebarExpanded)}> <button onClick={() => setSidebarExpanded(!sidebarExpanded)}>
<span className="sr-only">Expand / collapse sidebar</span> <span className="sr-only">Expand / collapse sidebar</span>
<svg className="w-6 h-6 fill-current sidebar-expanded:rotate-180" viewBox="0 0 24 24"> <svg
<path className="text-slate-400" d="M19.586 11l-5-5L16 4.586 23.414 12 16 19.414 14.586 18l5-5H7v-2z" /> className="w-6 h-6 fill-current sidebar-expanded:rotate-180"
viewBox="0 0 24 24"
>
<path
className="text-slate-400"
d="M19.586 11l-5-5L16 4.586 23.414 12 16 19.414 14.586 18l5-5H7v-2z"
/>
<path className="text-slate-600" d="M3 23H1V1h2z" /> <path className="text-slate-600" d="M3 23H1V1h2z" />
</svg> </svg>
</button> </button>

View File

@@ -1,18 +1,18 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
function SidebarLinkGroup({ function SidebarLinkGroup({ children, activecondition }) {
children,
activecondition,
}) {
const [open, setOpen] = useState(activecondition); const [open, setOpen] = useState(activecondition);
const handleClick = () => { const handleClick = () => {
setOpen(!open); setOpen(!open);
} };
return ( return (
<li className={`px-3 py-2 rounded-sm mb-0.5 last:mb-0 ${activecondition && 'bg-slate-900'}`}> <li
className={`px-3 py-2 rounded-sm mb-0.5 cursor-pointer last:mb-0 ${
activecondition && 'bg-slate-900'
}`}
>
{children(handleClick, open)} {children(handleClick, open)}
</li> </li>
); );

View File

@@ -1,123 +0,0 @@
import React from 'react';
import LineChart from '../../charts/LineChart03';
// Import utilities
import { tailwindConfig, hexToRGB } from '../../utils/Utils';
function AnalyticsCard01() {
const chartData = {
labels: [
'12-01-2020', '01-01-2021', '02-01-2021',
'03-01-2021', '04-01-2021', '05-01-2021',
'06-01-2021', '07-01-2021', '08-01-2021',
'09-01-2021', '10-01-2021', '11-01-2021',
'12-01-2021', '01-01-2022', '02-01-2022',
'03-01-2022', '04-01-2022', '05-01-2022',
'06-01-2022', '07-01-2022', '08-01-2022',
'09-01-2022', '10-01-2022', '11-01-2022',
'12-01-2022', '01-01-2023',
],
datasets: [
// Indigo line
{
label: 'Current',
data: [
5000, 8700, 7500, 12000, 11000, 9500, 10500,
10000, 15000, 9000, 10000, 7000, 22000, 7200,
9800, 9000, 10000, 8000, 15000, 12000, 11000,
13000, 11000, 15000, 17000, 18000,
],
fill: true,
backgroundColor: `rgba(${hexToRGB(tailwindConfig().theme.colors.blue[500])}, 0.08)`,
borderColor: tailwindConfig().theme.colors.indigo[500],
borderWidth: 2,
tension: 0,
pointRadius: 0,
pointHoverRadius: 3,
pointBackgroundColor: tailwindConfig().theme.colors.indigo[500],
clip: 20,
},
// Gray line
{
label: 'Previous',
data: [
8000, 5000, 6500, 5000, 6500, 12000, 8000,
9000, 8000, 8000, 12500, 10000, 10000, 12000,
11000, 16000, 12000, 10000, 10000, 14000, 9000,
10000, 15000, 12500, 14000, 11000,
],
borderColor: tailwindConfig().theme.colors.slate[300],
fill: false,
borderWidth: 2,
tension: 0,
pointRadius: 0,
pointHoverRadius: 3,
pointBackgroundColor: tailwindConfig().theme.colors.slate[300],
clip: 20,
},
],
};
return (
<div className="flex flex-col col-span-full xl:col-span-8 bg-white shadow-lg rounded-sm border border-slate-200">
<header className="px-5 py-4 border-b border-slate-100 flex items-center">
<h2 className="font-semibold text-slate-800">Analytics</h2>
</header>
<div className="px-5 py-1">
<div className="flex flex-wrap">
{/* Unique Visitors */}
<div className="flex items-center py-2">
<div className="mr-5">
<div className="flex items-center">
<div className="text-3xl font-bold text-slate-800 mr-2">24.7K</div>
<div className="text-sm font-medium text-emerald-500">+49%</div>
</div>
<div className="text-sm text-slate-500">Unique Visitors</div>
</div>
<div className="hidden md:block w-px h-8 bg-slate-200 mr-5" aria-hidden="true"></div>
</div>
{/* Total Pageviews */}
<div className="flex items-center py-2">
<div className="mr-5">
<div className="flex items-center">
<div className="text-3xl font-bold text-slate-800 mr-2">56.9K</div>
<div className="text-sm font-medium text-emerald-500">+7%</div>
</div>
<div className="text-sm text-slate-500">Total Pageviews</div>
</div>
<div className="hidden md:block w-px h-8 bg-slate-200 mr-5" aria-hidden="true"></div>
</div>
{/* Bounce Rate */}
<div className="flex items-center py-2">
<div className="mr-5">
<div className="flex items-center">
<div className="text-3xl font-bold text-slate-800 mr-2">54%</div>
<div className="text-sm font-medium text-amber-500">-7%</div>
</div>
<div className="text-sm text-slate-500">Bounce Rate</div>
</div>
<div className="hidden md:block w-px h-8 bg-slate-200 mr-5" aria-hidden="true"></div>
</div>
{/* Visit Duration*/}
<div className="flex items-center">
<div>
<div className="flex items-center">
<div className="text-3xl font-bold text-slate-800 mr-2">2m 56s</div>
<div className="text-sm font-medium text-amber-500">+7%</div>
</div>
<div className="text-sm text-slate-500">Visit Duration</div>
</div>
</div>
</div>
</div>
{/* Chart built with Chart.js 3 */}
<div className="grow">
{/* Change the height attribute to adjust the chart height */}
<LineChart data={chartData} width={800} height={300} />
</div>
</div>
);
}
export default AnalyticsCard01;

View File

@@ -1,139 +0,0 @@
import React from 'react';
import LineChart from '../../charts/LineChart04';
import { Link } from 'react-router-dom';
// Import utilities
import { tailwindConfig, hexToRGB } from '../../utils/Utils';
function AnalyticsCard02() {
const chartData = {
labels: [
'12-01-2020', '01-01-2021', '02-01-2021',
'03-01-2021', '04-01-2021', '05-01-2021',
'06-01-2021', '07-01-2021', '08-01-2021',
'09-01-2021', '10-01-2021', '11-01-2021',
'12-01-2021', '01-01-2022', '02-01-2022',
'03-01-2022', '04-01-2022', '05-01-2022',
'06-01-2022', '07-01-2022', '08-01-2022',
'09-01-2022', '10-01-2022', '11-01-2022',
'12-01-2022', '01-01-2023',
],
datasets: [
// Indigo line
{
data: [
732, 610, 610, 504, 504, 504, 349,
349, 504, 342, 504, 610, 391, 192,
154, 273, 191, 191, 126, 263, 349,
252, 423, 622, 470, 532,
],
fill: true,
backgroundColor: `rgba(${hexToRGB(tailwindConfig().theme.colors.blue[500])}, 0.08)`,
borderColor: tailwindConfig().theme.colors.indigo[500],
borderWidth: 2,
tension: 0,
pointRadius: 0,
pointHoverRadius: 3,
pointBackgroundColor: tailwindConfig().theme.colors.indigo[500],
clip: 20,
},
],
};
return (
<div className="flex flex-col col-span-full xl:col-span-4 bg-white shadow-lg rounded-sm border border-slate-200">
<header className="px-5 py-4 border-b border-slate-100">
<h2 className="font-semibold text-slate-800">Active Users Right Now</h2>
</header>
{/* Card content */}
<div className="flex flex-col h-full">
{/* Live visitors number */}
<div className="px-5 py-3">
<div className="flex items-center">
{/* Red dot */}
<div className="relative flex items-center justify-center w-4 h-4 rounded-full bg-rose-100 mr-3" aria-hidden="true">
<div className="absolute w-1.5 h-1.5 rounded-full bg-rose-500"></div>
</div>
{/* Vistors number */}
<div>
<div className="text-3xl font-bold text-slate-800 mr-2">347</div>
<div className="text-sm text-slate-500">Live visitors</div>
</div>
</div>
</div>
{/* Chart built with Chart.js 3 */}
<div >
{/* Change the height attribute to adjust the chart height */}
<LineChart data={chartData} width={389} height={70} />
</div>
{/* Table */}
<div className="grow px-5 pt-3 pb-1">
<div className="overflow-x-auto">
<table className="table-auto w-full">
{/* Table header */}
<thead className="text-xs uppercase text-slate-400">
<tr>
<th className="py-2">
<div className="font-semibold text-left">Top pages</div>
</th>
<th className="py-2">
<div className="font-semibold text-right">Active users</div>
</th>
</tr>
</thead>
{/* Table body */}
<tbody className="text-sm divide-y divide-slate-100">
{/* Row */}
<tr>
<td className="py-2">
<div className="text-left">preview.cruip.com/open-pro/</div>
</td>
<td className="py-2">
<div className="font-medium text-right text-slate-800">94</div>
</td>
</tr>
{/* Row */}
<tr>
<td className="py-2">
<div className="text-left">preview.cruip.com/simple/</div>
</td>
<td className="py-2">
<div className="font-medium text-right text-slate-800">42</div>
</td>
</tr>
{/* Row */}
<tr>
<td className="py-2">
<div className="text-left">cruip.com/unlimited/</div>
</td>
<td className="py-2">
<div className="font-medium text-right text-slate-800">12</div>
</td>
</tr>
{/* Row */}
<tr>
<td className="py-2">
<div className="text-left">preview.cruip.com/twist/</div>
</td>
<td className="py-2">
<div className="font-medium text-right text-slate-800">4</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
{/* Card footer */}
<div className="text-right px-5 pb-4">
<Link className="text-sm font-medium text-indigo-500 hover:text-indigo-600" to="#0">Real-Time Report -&gt;</Link>
</div>
</div>
</div>
);
}
export default AnalyticsCard02;

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