35 Commits

Author SHA1 Message Date
Daria Golova
0d4f27660a Merge branch 'DID-5' 2022-07-01 17:27:39 +03:00
Daria Golova
9703371f28 made a reference book 2022-07-01 17:16:09 +03:00
Daria Golova
16ca8a1df2 converted user data into Map object 2022-07-01 16:03:21 +03:00
Daria Golova
e883b190a7 converted user data into Map object 2022-07-01 16:01:20 +03:00
Daria Golova
85897a8efe changed name of the method in store.js 2022-06-30 14:50:59 +03:00
Daria Golova
5f85d1696a integrated received data into table 2022-06-30 14:42:59 +03:00
Daria Golova
1c539e5255 made userDataStore 2022-06-30 14:42:59 +03:00
DuCay
bcfc018560 Merge pull request #25 from franze6/DID-4
Did 4
2022-06-30 13:35:58 +03:00
DwCay
e175ca0295 fix naming for post method 2022-06-30 13:35:07 +03:00
DwCay
bac246eac2 fix label name for post request 2022-06-30 13:17:10 +03:00
DwCay
f65c99e86a fix store 2022-06-29 19:35:06 +03:00
DwCay
63439b2da1 Add method post encrypt auth data 2022-06-29 18:47:49 +03:00
Kirill Andrusyak
1d9368d200 Merge pull request #24 from franze6/DID-3
Доработка формы создания поля
2022-06-28 17:01:22 +03:00
kandrusyak
3a4ed9daf9 auth 2022-06-28 16:38:05 +03:00
Daria Golova
54ccd3ed9f fix popup hint 2022-06-23 18:52:19 +03:00
Daria Golova
fe1105e9d6 delete useEffect 2022-06-23 15:28:29 +03:00
Daria Golova
95cc2ac1e0 fix property field, added default value of seed 2022-06-23 15:21:08 +03:00
Daria Golova
b4516ef18e made a popup hint 2022-06-22 19:36:58 +03:00
kandrusyak
d617de75b5 router fix 2022-06-22 14:53:31 +03:00
Kirill Andrusyak
83e5e39d3c Merge pull request #21 from franze6/DID-2
Создание fetch wrapper
2022-06-21 11:47:58 +03:00
Kirill Andrusyak
f89d59e7df Merge branch 'master' into DID-2 2022-06-21 11:47:49 +03:00
Kirill Andrusyak
c944644db0 Merge pull request #23 from franze6/DID-1
Did 1
2022-06-21 11:44:57 +03:00
DwCay
bfdddee46a Add method for get passPhrase and generation hex for account logo 2022-06-18 16:50:32 +03:00
kandrusyak
0da68ddd63 migrate to webpack 2022-06-17 16:56:30 +03:00
Daria Golova
f209ee5f5f added token availability check 2022-06-16 16:05:48 +03:00
Daria Golova
9bef3d9311 fix authorization by token 2022-06-16 15:25:25 +03:00
Daria Golova
632b98a81a added authorization by token 2022-06-16 14:30:41 +03:00
DwCay
eeaabeebf3 Add method for get passPhrase 2022-06-15 17:41:02 +03:00
Daria Golova
81f5ec2741 fix methods 2022-06-15 14:36:23 +03:00
Daria Golova
3fcda54070 fix wrapper 2022-06-15 14:21:39 +03:00
Daria Golova
49aff14a80 create fetch wrapper 2022-06-14 13:53:28 +03:00
DwCay
da04efbf80 Add registration store 2022-06-14 13:13:58 +03:00
DuCay
541c5988e9 Merge pull request #20 from franze6/Deleting_elements
Delete Onboarding and Component from Sidebar
2022-06-02 15:53:43 +03:00
DwCay
cfa5af78c0 Delete Onboarding and Component from Sidebar 2022-06-02 15:53:06 +03:00
DuCay
fc4d9fb897 Merge pull request #19 from franze6/Validation_merged
Update Validate Panel
2022-06-02 15:35:15 +03:00
30 changed files with 12954 additions and 1654 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
# dependencies # dependencies
/node_modules /node_modules
.idea

View File

@@ -1,26 +0,0 @@
# Mosaic React
React landing page template designed by Cruip.
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run dev
```
### Compiles and minifies for production
```
npm run build
```
### Customize configuration
See [Configuration Reference](https://vitejs.dev/guide/).
## Support notes
We are shipping our templates with a very basic React configuration to let you quickly get into the development process, but we don't discourage you from using any other configuration or framework built on the top of React. So, please note that any request dealing with React (e.g. extra features, customisations, et cetera) is to be considered out of the support scope.
For more information about what support covers, please see our (FAQs)[https://cruip.com/faq/].

View File

@@ -1,21 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body class="font-inter antialiased bg-slate-100 text-slate-600">
<noscript>You need to enable JavaScript to run this app.</noscript>
<script>
if (localStorage.getItem('sidebar-expanded') == 'true') {
document.querySelector('body').classList.add('sidebar-expanded');
} else {
document.querySelector('body').classList.remove('sidebar-expanded');
}
</script>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

13297
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,27 +2,60 @@
"name": "mosaic-react", "name": "mosaic-react",
"version": "0.1.0", "version": "0.1.0",
"scripts": { "scripts": {
"dev": "vite", "dev": "webpack-dev-server",
"build": "vite build", "build": "webpack"
"preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@liskhq/lisk-client": "^5.2.2",
"@tailwindcss/forms": "^0.4.0", "@tailwindcss/forms": "^0.4.0",
"autoprefixer": "^10.4.5",
"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",
"crypto-browserify": "^3.12.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0",
"mobx": "^6.6.0",
"mobx-react-lite": "^3.4.0",
"moment": "^2.29.1", "moment": "^2.29.1",
"net": "^1.0.2",
"os-browserify": "^0.3.0",
"path-browserify": "^1.0.1",
"postcss-loader": "^7.0.0",
"process": "^0.11.10",
"querystring-es3": "^0.2.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-flatpickr": "^3.10.7", "react-flatpickr": "^3.10.7",
"react-router-dom": "^6.2.1", "react-router-dom": "^6.2.1",
"react-transition-group": "^4.4.1" "react-transition-group": "^4.4.1",
"stream-browserify": "^3.0.0",
"url": "^0.11.0",
"util": "^0.12.4"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-react": "^1.0.7", "@babel/core": "^7.18.5",
"autoprefixer": "^10.4.2", "@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.17.12",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"html-loader": "^3.1.2",
"postcss": "^8.4.5", "postcss": "^8.4.5",
"sass": "^1.49.0", "sass": "^1.52.3",
"sass-loader": "^13.0.0",
"style-loader": "^3.3.1",
"tailwindcss": "^3.0.18", "tailwindcss": "^3.0.18",
"vite": "^2.7.2" "webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.2"
},
"babel": {
"presets": [
"@babel/env",
"@babel/react"
]
},
"browser": {
"fs": false
} }
} }

View File

@@ -1,7 +1,7 @@
import tailwind from 'tailwindcss' const tailwind = require('tailwindcss')
import autoprefixer from 'autoprefixer' const autoprefixer = require('autoprefixer')
import tailwindConfig from './src/css/tailwind.config.js' const tailwindConfig = require('./src/tailwind.config.js')
export default { module.exports = {
plugins: [tailwind(tailwindConfig), autoprefixer], plugins: [tailwind(tailwindConfig), autoprefixer],
} }

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

11
public/index.html Normal file
View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
import { import {
Routes, Routes,
Route, Route,
useLocation useLocation,
} from 'react-router-dom'; } from 'react-router-dom';
import './css/style.scss'; import './css/style.scss';

3
src/api/node.js Normal file
View File

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

View File

@@ -1,12 +1,14 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
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 {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg";
import UserAvatar from '../images/user-avatar-32.png'; import UserAvatar from '../images/user-avatar-32.png';
function DropdownProfile({ const DropdownProfile = observer(({
align align
}) { })=>{
const [dropdownOpen, setDropdownOpen] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false);
@@ -43,7 +45,7 @@ function DropdownProfile({
onClick={() => setDropdownOpen(!dropdownOpen)} onClick={() => setDropdownOpen(!dropdownOpen)}
aria-expanded={dropdownOpen} aria-expanded={dropdownOpen}
> >
<img className="w-8 h-8 rounded-full" src={localStorage.getItem('svgAvatar') || UserAvatar} width="32" height="32" alt="User" /> <img className="w-8 h-8 rounded-full" src={generateSvgAvatar(registrationStore.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">Acme Inc.</span>
<svg className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400" viewBox="0 0 12 12"> <svg className="w-3 h-3 shrink-0 ml-1 fill-current text-slate-400" viewBox="0 0 12 12">
@@ -95,6 +97,6 @@ function DropdownProfile({
</Transition> </Transition>
</div> </div>
) )
} })
export default DropdownProfile; export default DropdownProfile;

View File

@@ -3,7 +3,6 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
// Additional styles
@import 'additional-styles/utility-patterns.scss'; @import 'additional-styles/utility-patterns.scss';
@import 'additional-styles/range-slider.scss'; @import 'additional-styles/range-slider.scss';
@import 'additional-styles/toggle-switch.scss'; @import 'additional-styles/toggle-switch.scss';

View File

@@ -35,13 +35,10 @@ function createBlob(options) {
return path + "z"; return path + "z";
} }
export var generateSvgAvatar = (function () { export var generateSvgAvatar = ( function () {
return function (seed, raw) { return function (publicKey, seed, raw) {
let pubKey = localStorage.getItem('svgKey') || 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"
if (!localStorage.getItem('svgKey')) {
localStorage.setItem('svgKey', `${pubKey}`)
}
let particles = []; let particles = [];
let gradients = ['#000000']; let gradients = ['#000000'];
let defs = []; let defs = [];

View File

@@ -1,8 +1,11 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import { BrowserRouter as Router } from 'react-router-dom' import {Buffer} from "buffer";
import { HashRouter as Router } from 'react-router-dom'
import App from './App' import App from './App'
globalThis.Buffer = Buffer
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<Router> <Router>

View File

@@ -1,4 +1,4 @@
import React, {useEffect, 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';
@@ -19,9 +19,6 @@ import DashboardCard10 from '../partials/dashboard/DashboardCard10';
import DashboardCard11 from '../partials/dashboard/DashboardCard11'; import DashboardCard11 from '../partials/dashboard/DashboardCard11';
function Dashboard() { function Dashboard() {
useEffect(() => {
localStorage.setItem('svgKey', '')
}, [])
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
return ( return (

View File

@@ -1,11 +1,20 @@
import React 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 {passphrase} from "@liskhq/lisk-client";
import {registrationStore} from "../store/store";
const Onboarding1 = observer(()=>{
let phrase = passphrase.Mnemonic.generateMnemonic()
registrationStore.savePassPhrase(phrase)
useEffect(()=>{
},[])
function Onboarding1() {
return ( return (
<main className="bg-white"> <main className="bg-white">
@@ -127,6 +136,6 @@ function Onboarding1() {
</main> </main>
); );
} })
export default Onboarding1; export default Onboarding1;

View File

@@ -1,11 +1,54 @@
import React from 'react'; import React, {useEffect, useState} from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { observer } from "mobx-react-lite";
import { registrationStore } 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";
function Onboarding2() { const Onboarding2 = observer(()=>{
const [dataRegistration, setDataRegistration] = useState(
{
first_name: '',
second_name: '',
gender: '',
birthdate: '',
}
)
const [fillingForm, setFillingForm] = useState(false)
const saveValueChange = (event)=>{
const newValue=event.target.value;
setDataRegistration({
...dataRegistration,
[event.target.id]: newValue
})
}
const saveStoreRegistration = ()=>{
Object.keys(dataRegistration).forEach(item=>{
let element=document.getElementById(item)
if(dataRegistration[item]) {
element.style.borderColor=""
} else {
element.style.borderColor="red"
}
})
if(Object.keys(dataRegistration).find(item=>!dataRegistration[item])) {
return
} else {
setFillingForm(true)
}
registrationStore.saveDataRegistration(dataRegistration)
}
useEffect(()=>{
if(fillingForm===true) {
document.getElementById("signup").click()
}
},[fillingForm])
return ( return (
<main className="bg-white"> <main className="bg-white">
@@ -60,23 +103,24 @@ function Onboarding2() {
<form> <form>
<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="email">First name <span className="text-rose-500">*</span></label> <label className="block text-sm font-medium mb-1" htmlFor="first_name">First name <span className="text-rose-500">*</span></label>
<input id="email" className="form-input w-full" type="email" /> <input id="first_name" onChange={(event)=>saveValueChange(event)} className="form-input w-full" type="text" />
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="name">Last name <span className="text-rose-500">*</span></label> <label className="block text-sm font-medium mb-1" htmlFor="second_name">Last name <span className="text-rose-500">*</span></label>
<input id="name" className="form-input w-full" type="text" /> <input id="second_name" onChange={(event)=>saveValueChange(event)} className="form-input w-full" type="text" />
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="role">Gender <span className="text-rose-500">*</span></label> <label className="block text-sm font-medium mb-1" htmlFor="gender">Gender <span className="text-rose-500">*</span></label>
<select id="role" className="form-select w-full"> <select id="gender" onChange={(event)=>saveValueChange(event)} className="form-select w-full">
<option className="hidden"></option>
<option>male</option> <option>male</option>
<option>feemale</option> <option>feemale</option>
</select> </select>
</div> </div>
<div> <div>
<label className="block text-sm font-medium mb-1" htmlFor="password">Date of birth <span className="text-rose-500">*</span></label> <label className="block text-sm font-medium mb-1" htmlFor="birthdate">Date of birth <span className="text-rose-500">*</span></label>
<input id="password" className="form-input w-full" type="password" autoComplete="on" /> <input id="birthdate" onChange={(event)=>saveValueChange(event)} className="form-input w-full" type="date" autoComplete="on" />
</div> </div>
</div> </div>
<div className="flex items-center justify-between mt-6"> <div className="flex items-center justify-between mt-6">
@@ -86,8 +130,8 @@ function Onboarding2() {
<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 className="btn bg-indigo-500 hover:bg-indigo-600 text-white ml-3 whitespace-nowrap"> <button onClick={()=>saveStoreRegistration()} className="btn bg-indigo-500 hover:bg-indigo-600 text-white ml-3 whitespace-nowrap">
<Link to="/onboarding-3">Sign Up</Link> <Link id="signup" to={fillingForm && "/onboarding-3"} >Sign Up</Link>
</button> </button>
</div> </div>
</form> </form>
@@ -114,6 +158,6 @@ function Onboarding2() {
</main> </main>
); );
} });
export default Onboarding2; export default Onboarding2;

View File

@@ -1,10 +1,34 @@
import React 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 {registrationStore} from "../store/store";
import { passphrase } from "@liskhq/lisk-client";
function Onboarding3() { function Onboarding3() {
const arrayBadges = ['judge', 'jelly', 'wasp', 'true', 'clog', 'forward', 'talent', 'ozone', 'belive', 'fresh', 'bulk', 'hobby'] const [passPhrase, setPassPhrase] = useState(registrationStore.passPhrase.replace(/\s+/g, ' ').split(' ').map((item, index) => ({str:item, id:index })))
function generatePassPhrase() {
let phrase = passphrase.Mnemonic.generateMnemonic()
registrationStore.savePassPhrase(phrase)
let newPassPhrase = registrationStore.passPhrase.split(' ').map((item, index) => ({str:item, id:index }))
setPassPhrase(newPassPhrase)
}
function savePassPhraseInStore (copiedPhrase) {
let phrasePaste = copiedPhrase.trim().replace(/\s+/g, ' ').split(' ')
if(phrasePaste.length===12) {
registrationStore.savePassPhrase(copiedPhrase)
setPassPhrase(phrasePaste.map((item, index) => ({str:item, id:index })))
} else {
return false
}
}
function pastePassPhrase() {
navigator.clipboard.readText().then(res=>savePassPhraseInStore(res))
}
return ( return (
<main className="bg-white"> <main className="bg-white">
@@ -59,21 +83,21 @@ function Onboarding3() {
<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">
{arrayBadges.map(tag => ( {passPhrase.map(tag => (
<button key={tag} 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}</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 className="btn pl-9 pr-16 border-slate-200 hover:border-slate-300 text-slate-600"> <button onClick={()=>pastePassPhrase()} className="btn pl-9 pr-16 border-slate-200 hover:border-slate-300 text-slate-600">
<svg className="w-4 h-4 fill-current text-slate-400 shrink-0 mb-0.5" viewBox="0 0 16 16"> <svg className="w-4 h-4 fill-current text-slate-400 shrink-0 mb-0.5" viewBox="0 0 16 16">
<path <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> 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 className="btn pr-11 pl-7 border-slate-200 hover:border-slate-300 text-rose-500"> <button onClick={()=>generatePassPhrase()} className="btn pr-11 pl-7 border-slate-200 hover:border-slate-300 text-rose-500">
<svg className="mb-0.5" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <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 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"/> <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"/>
@@ -99,7 +123,7 @@ function Onboarding3() {
{/* 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 className="object-cover object-center" src={generateSvgAvatar(registrationStore.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>

View File

@@ -1,14 +1,31 @@
import React from 'react'; import React, {useEffect, 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 { registrationStore } from "../store/store";
function Onboarding4() { const Onboarding4 = observer(()=>{
function saveDigitalId() { const [checkBoxesSelected, setCheckBoxesSelected] = useState(false)
localStorage.setItem('svgAvatar', generateSvgAvatar())
function createAccount () {
let checkBoxes=[...document.getElementsByClassName("form-checkbox")]
if(checkBoxes.find(item=>item.checked===false)) {
return
} else {
registrationStore.fetchCreateNewAccount()
setCheckBoxesSelected(true)
}
} }
useEffect(()=>{
if(checkBoxesSelected===true) {
document.getElementById("link-dashboard").click()
}
},[checkBoxesSelected])
return ( return (
<main className="bg-white"> <main className="bg-white">
@@ -64,9 +81,9 @@ function Onboarding4() {
<circle className="text-emerald-100" cx="32" cy="32" r="32" /> <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" /> <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">Nice to meet you, Ivan 🙌</h1> <h1 className="text-3xl text-slate-800 font-bold mb-8">{registrationStore.accountData.first_name ? `Nice to meet you, ${registrationStore.accountData.first_name} 🙌` : 'Please, go back step 2'}</h1>
<button onClick={()=>saveDigitalId()} className="btn px-6 bg-indigo-500 hover:bg-indigo-600 text-white"> <button onClick={()=>createAccount()} className="btn px-6 bg-indigo-500 hover:bg-indigo-600 text-white">
<Link to="/dashboard">Go To Profile -&gt;</Link> <Link id="link-dashboard" to={checkBoxesSelected && "/dashboard"}>Go To Profile -&gt;</Link>
</button> </button>
</div> </div>
@@ -95,7 +112,7 @@ function Onboarding4() {
{/* 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 className="object-cover object-center" src={generateSvgAvatar(registrationStore.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>
@@ -104,6 +121,6 @@ function Onboarding4() {
</main> </main>
); );
} });
export default Onboarding4; export default Onboarding4;

View File

@@ -1,14 +1,12 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { userDataStore } from '../../store/userDataStore';
import Sidebar from '../../partials/Sidebar'; import Sidebar from '../../partials/Sidebar';
import { observer } from 'mobx-react-lite';
import Header from '../../partials/Header'; import Header from '../../partials/Header';
import ProfileTable from '../../partials/digitalId/ProfileTable'; import ProfileTable from '../../partials/digitalId/ProfileTable';
import Image from '../../images/transactions-image-04.svg'; import Image from '../../images/transactions-image-04.svg';
import ProfileIcon from '../../images/profile-icon.svg';
import Avatar01 from '../../images/avatar-01.jpg';
import Avatar02 from '../../images/avatar-02.jpg';
import Avatar03 from '../../images/avatar-03.jpg';
function Profile () { const Profile = observer (() => {
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
const [buttonPanelOpen, setButtonPanelOpen] = useState(true); const [buttonPanelOpen, setButtonPanelOpen] = useState(true);
const [removePanelOpen, setRemovePanelOpen] = useState(false); const [removePanelOpen, setRemovePanelOpen] = useState(false);
@@ -17,6 +15,34 @@ function Profile () {
const [addPanelOpen, setAddPanelOpen] = useState(false); const [addPanelOpen, setAddPanelOpen] = useState(false);
const [selectedItems, setSelectedItems] = useState([]); const [selectedItems, setSelectedItems] = useState([]);
const defaultValues = {
property: '',
value: '',
seed: Math.floor(Math.random() * 90000000000000000000)
};
const initialPropertyValues = ['First name', 'Second name', 'Birthdate', 'Gender', 'Residence', 'Document type', 'Document ID', 'Issue date', 'Expiry date'];
const [propertyValues, setPropertyValues] = useState([]);
const [currentValues, setCurrentValues] = useState(defaultValues);
const openHint = () => {
if (!currentValues.property) {
setPropertyValues(initialPropertyValues);
}
}
const changeCurrentValues = (value, field) => {
setCurrentValues((prevState)=> ({
...prevState,
[field]: value.charAt(0).toUpperCase()+value.slice(1),
}))
}
const closeHint = (element) => {
changeCurrentValues(element, 'property');
setPropertyValues([]);
}
const handleSelectedItems = (selectedItems) => { const handleSelectedItems = (selectedItems) => {
setSelectedItems([...selectedItems]); setSelectedItems([...selectedItems]);
if (selectedItems.length > 0) { if (selectedItems.length > 0) {
@@ -39,7 +65,16 @@ function Profile () {
setButtonPanelOpen(false); setButtonPanelOpen(false);
} }
const sendValues = () => {
if (!userDataStore.decryptedData.some((element) => element.property === currentValues.property) && (currentValues.seed.toString().length === 20)) {
setAddPanelOpen(false);
setButtonPanelOpen(true);
setCurrentValues(defaultValues);
}
}
const cancelAddPanel = () => { const cancelAddPanel = () => {
setCurrentValues(defaultValues);
setAddPanelOpen(false); setAddPanelOpen(false);
setButtonPanelOpen(true); setButtonPanelOpen(true);
} }
@@ -54,140 +89,14 @@ function Profile () {
setButtonPanelOpen(true); setButtonPanelOpen(true);
} }
const userData = [ const handleInput = (text, field) => {
{ changeCurrentValues(text, field);
id: '0', setPropertyValues(initialPropertyValues);
category: 'General', if (text) {
validated: true, let reg = new RegExp(`^${text}`, 'img');
blockchained: true, setPropertyValues((prevState) => prevState.filter((element) => element.match(reg)));
image: ProfileIcon, }
value: 'Dominik', };
property: 'First name',
status: 'Completed',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: [Avatar01, Avatar02, Avatar03]
},
{
id: '1',
category: 'General',
validated: true,
blockchained: true,
image: ProfileIcon,
value: 'Lamakani',
property: 'Second name',
status: 'Progress',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: null
},
{
id: '2',
category: 'General',
validated: true,
blockchained: true,
image: ProfileIcon,
value: '05.10.1983',
property: 'Birthdate',
status: 'Incorrect',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: null
},
{
id: '3',
category: 'General',
validated: true,
blockchained: true,
image: ProfileIcon,
value: 'Male',
property: 'Gender',
status: 'Stored',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: null
},
{
id: '4',
category: 'Nationality',
validated: true,
blockchained: true,
image: ProfileIcon,
value: 'United Kindom',
property: 'Residence',
status: 'Completed',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: [Avatar01, Avatar02, Avatar03]
},
{
id: '5',
category: 'Nationality',
validated: true,
blockchained: true,
image: ProfileIcon,
value: 'Passport',
property: 'Document type',
status: 'Progress',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: [Avatar01, Avatar02, Avatar03]
},
{
id: '6',
category: 'Nationality',
validated: true,
blockchained: true,
image: ProfileIcon,
value: 'A123B3143',
property: 'Document ID',
status: 'Incorrect',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: null
},
{
id: '7',
category: 'Nationality',
validated: true,
blockchained: true,
image: ProfileIcon,
value: '05.10.2012',
property: 'Issue date',
status: 'Stored',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: null
},
{
id: '8',
category: 'Nationality',
validated: true,
blockchained: true,
image: ProfileIcon,
value: '05.10.2032',
property: 'Expiry date',
status: 'Stored',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: null
},
{
id: '9',
category: 'Social',
validated: true,
blockchained: true,
image: ProfileIcon,
value: '@lamakani',
property: 'Telegram',
status: 'Completed',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: null
},
{
id: '10',
category: 'Social',
validated: true,
blockchained: true,
image: ProfileIcon,
value: '@lamakani',
property: 'Telegram',
status: 'Progress',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: [Avatar01, Avatar02, Avatar03]
},
]
return ( return (
<div className="flex h-screen overflow-hidden"> <div className="flex h-screen overflow-hidden">
@@ -222,7 +131,7 @@ function Profile () {
</ul> </ul>
</div> </div>
{/* Table */} {/* Table */}
<ProfileTable selectedItems={handleSelectedItems} userData={userData}/> <ProfileTable selectedItems={handleSelectedItems} userData={userDataStore.decryptedData}/>
</div> </div>
{/* Left sidebar */} {/* Left sidebar */}
<div> <div>
@@ -246,22 +155,56 @@ function Profile () {
<div className="flex flex-col gap-y-3"> <div 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 id="property" className="form-input w-full" type="text" required /> <input
autoComplete='off'
id="property"
className="form-input w-full"
type="text"
required
onInput={(e) => handleInput(e.target.value, 'property')}
value={currentValues.property}
onClick={openHint}
/>
<div className={`flex flex-col gap-y-2 py-2 pl-2 border border-t-0 border-slate-200 rounded ${(propertyValues.length > 0) || 'hidden'}`}>
<ul>
{propertyValues.map((element, index) => (
<li key={index} onClick={() => closeHint(element)}>{element}</li>
))}
</ul>
</div>
</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 id="value" className="form-input w-full" type="text" required /> <input
autoComplete='off'
id="value"
className="form-input w-full"
type="text"
required
onClick={() => setPropertyValues([])}
onChange={(e) => changeCurrentValues(e.target.value, 'value')}
value={currentValues.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 id="seed" className="form-input w-full" type="text" required /> <input
autoComplete='off'
id="seed"
className="form-input w-full"
type="text"
required
onClick={() => setPropertyValues([])}
onChange={(e) => changeCurrentValues(e.target.value, 'seed')}
value={currentValues.seed}
/>
</div> </div>
</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="switch-1" className="sr-only" checked={toggle} onChange={() => setToggle(!toggle)} /> <input type="checkbox" id="blockcheined" className="sr-only" checked={toggle} onChange={() => setToggle(!toggle)} />
<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>
@@ -278,7 +221,7 @@ function Profile () {
</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"
onClick={cancelAddPanel} onClick={sendValues}
> >
Send Send
</button> </button>
@@ -288,8 +231,8 @@ function Profile () {
<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 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> <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">
{userData.filter(({ id }) => selectedItems.includes(id)).map(item => ( {userDataStore.decryptedData.filter(({ label }) => selectedItems.includes(label)).map(item => (
<span key={item.id} className="text-sm font-normal text-slate-600 py-3 border-b border-slate-200">{item.property}</span> <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">
@@ -390,6 +333,6 @@ function Profile () {
</div> </div>
</div> </div>
) )
} })
export default Profile; export default Profile

View File

@@ -2,7 +2,7 @@ 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';
function Verify () { const Verify = (() => {
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
const [descriptionOpen, setDescriptionOpen] = useState(false); const [descriptionOpen, setDescriptionOpen] = useState(false);
@@ -165,6 +165,6 @@ function Verify () {
</div> </div>
</div> </div>
) )
} })
export default Verify; export default Verify;

View File

@@ -47,10 +47,6 @@ function Sidebar({
} }
}, [sidebarExpanded]); }, [sidebarExpanded]);
function saveDigitalId () {
localStorage.setItem('digitalId', '7234ABC3423423523457234ABC34234')
}
return ( return (
<div> <div>
{/* Sidebar backdrop (mobile only) */} {/* Sidebar backdrop (mobile only) */}
@@ -340,193 +336,6 @@ function Sidebar({
</li> </li>
</ul> </ul>
</div> </div>
{/* More group */}
<div>
<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>
<span className="lg:hidden lg:sidebar-expanded:block 2xl:block">More</span>
</h3>
<ul className="mt-3">
{/* Onboarding */}
<SidebarLinkGroup>
{(handleClick, open) => {
return (
<React.Fragment>
<a
href="#0"
className={`block text-slate-200 hover:text-white truncate transition duration-150 ${open && '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-600"
d="M19 5h1v14h-2V7.414L5.707 19.707 5 19H4V5h2v11.586L18.293 4.293 19 5Z"
/>
<path
className="fill-current text-slate-400"
d="M5 9a4 4 0 1 1 0-8 4 4 0 0 1 0 8Zm14 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8ZM5 23a4 4 0 1 1 0-8 4 4 0 0 1 0 8Zm14 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8Z"
/>
</svg>
<span className="text-sm font-medium ml-3 lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Onboarding
</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="/onboarding-1" className="block text-slate-400 hover:text-slate-200 transition duration-150 truncate">
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Step 1
</span>
</NavLink>
</li>
<li className="mb-1 last:mb-0">
<NavLink end to="/onboarding-2" className="block text-slate-400 hover:text-slate-200 transition duration-150 truncate">
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Step 2
</span>
</NavLink>
</li>
<li className="mb-1 last:mb-0">
<NavLink end to="/onboarding-3" className="block text-slate-400 hover:text-slate-200 transition duration-150 truncate">
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Step 3
</span>
</NavLink>
</li>
<li className="mb-1 last:mb-0">
<NavLink end to="/onboarding-4" className="block text-slate-400 hover:text-slate-200 transition duration-150 truncate">
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Step 4
</span>
</NavLink>
</li>
<li className="mb-1 last:mb-0">
<NavLink onClick={()=>saveDigitalId()} end to="/shared-data" className="block text-slate-400 hover:text-slate-200 transition duration-150 truncate">
<span className="text-sm font-medium lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Shared Data
</span>
</NavLink>
</li>
</ul>
</div>
</React.Fragment>
);
}}
</SidebarLinkGroup>
{/* Components */}
<SidebarLinkGroup activecondition={pathname.includes('component')}>
{(handleClick, open) => {
return (
<React.Fragment>
<a
href="#0"
className={`block text-slate-200 hover:text-white truncate transition duration-150 ${
pathname.includes('component') && '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">
<circle
className={`fill-current text-slate-600 ${pathname.includes('component') && 'text-indigo-500'}`}
cx="16"
cy="8"
r="8"
/>
<circle
className={`fill-current text-slate-400 ${pathname.includes('component') && 'text-indigo-300'}`}
cx="8"
cy="16"
r="8"
/>
</svg>
<span className="text-sm font-medium ml-3 lg:opacity-0 lg:sidebar-expanded:opacity-100 2xl:opacity-100 duration-200">
Components
</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="/component/button"
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">
Button
</span>
</NavLink>
</li>
<li className="mb-1 last:mb-0">
<NavLink
end
to="/component/badge"
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">
Badge
</span>
</NavLink>
</li>
<li className="mb-1 last:mb-0">
<NavLink
end
to="/component/accordion"
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">
Accordion
</span>
</NavLink>
</li>
</ul>
</div>
</React.Fragment>
);
}}
</SidebarLinkGroup>
</ul>
</div>
</div> </div>
{/* Expand / collapse button */} {/* Expand / collapse button */}

View File

@@ -1,5 +1,6 @@
import React, {useState, useEffect} from 'react'; import React, {useState, useEffect} from 'react';
import ProfileTableItem from './ProfileTableItem'; import ProfileTableItem from './ProfileTableItem';
import ProfileIcon from '../../images/profile-icon.svg';
function ProfileTable({ function ProfileTable({
selectedItems, selectedItems,
@@ -8,6 +9,13 @@ function ProfileTable({
const [isCheck, setIsCheck] = useState([]); const [isCheck, setIsCheck] = useState([]);
const defaultData = {
image: ProfileIcon,
status: 'Stored',
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
avatars: null
}
useEffect(() => { useEffect(() => {
selectedItems(isCheck); selectedItems(isCheck);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -21,85 +29,123 @@ function ProfileTable({
} }
}; };
const data = {
general:["First name","Second name", "Gender", "Birthdate", "Place of birth"],
nationality:["Nationality", "National id", "National doctype", "National doc id", "National doc issue date", "National doc expiry date"],
social:["Telephone", "Twitter", "Facebook", "Instagram", "Youtube", "Wechat", "Tiktok", "Linkedin", "Vk", "Github", "Telegram", "Qq", "Snapchat", "Reddit"]
}
const generalData = userData.filter(elem => data.general.includes(elem.label)),
nationalityData = userData.filter(elem => data.nationality.includes(elem.label)),
sosialData = userData.filter(elem => data.nationality.includes(elem.label)),
otherData = userData.filter((elem) =>
!data.general.includes(elem.label) && !data.nationality.includes(elem.label) && !data.social.includes(elem.label));
return ( return (
<> <>
{/* General */} {/* General */}
<h2 className="grow text-base font-semibold text-slate-800 truncate mb-2.5 mt-8">General 🖋</h2> <div className={`${generalData.length > 0 || 'hidden'}`}>
<div className="overflow-x-auto w-[828px]"> <h2 className="grow text-base font-semibold text-slate-800 truncate mb-2.5 mt-8">General 🖋</h2>
<table className="w-[828px] table-auto w-full"> <div className="overflow-x-auto w-[828px]">
<tbody className="divide-slate-200 divide-y"> <table className="w-[828px] table-auto w-full">
{userData.filter(data => data.category === 'General') <tbody className="divide-slate-200 divide-y">
.map(data => { {generalData.map((data) => {
return ( return (
<ProfileTableItem <ProfileTableItem
key={data.id} key={data.label}
id={data.id} id={data.label}
image={data.image} image={defaultData.image}
value={data.value} value={data.value.charAt(0).toUpperCase()+data.value.slice(1)}
property={data.property} property={data.label}
status={data.status} status={defaultData.status}
transactions={data.transactions} transactions={defaultData.transactions}
avatars={data.avatars} avatars={defaultData.avatars}
handleClick={handleClick} handleClick={handleClick}
isChecked={isCheck.includes(data.id)} isChecked={isCheck.includes(data.label)}
/> />
) )
}) })}
} </tbody>
</tbody> </table>
</table> </div>
</div> </div>
{/* Nationality */} {/* Nationality */}
<h2 className="grow text-base font-semibold text-slate-800 truncate mb-2.5 mt-8">Nationality 🖋</h2> <div className={`${nationalityData.length > 0 || 'hidden'}`}>
<div className="overflow-x-auto w-[828px]"> <h2 className="grow text-base font-semibold text-slate-800 truncate mb-2.5 mt-8">Nationality 🖋</h2>
<table className="w-[828px] table-auto w-full"> <div className="overflow-x-auto w-[828px]">
<tbody className="text-sm divide-slate-200 divide-y "> <table className="w-[828px] table-auto w-full">
{userData.filter(data => data.category === 'Nationality') <tbody className="text-sm divide-slate-200 divide-y ">
.map(data => { {nationalityData.map(data => {
return ( return (
<ProfileTableItem <ProfileTableItem
key={data.id} key={data.label}
id={data.id} id={data.label}
image={data.image} image={defaultData.image}
value={data.value} value={data.value.charAt(0).toUpperCase()+data.value.slice(1)}
property={data.property} property={data.label}
status={data.status} status={defaultData.status}
transactions={data.transactions} transactions={defaultData.transactions}
avatars={data.avatars} avatars={defaultData.avatars}
handleClick={handleClick} handleClick={handleClick}
isChecked={isCheck.includes(data.id)} isChecked={isCheck.includes(data.label)}
/> />
) )
}) })}
} </tbody>
</tbody> </table>
</table> </div>
</div> </div>
{/* Social */} {/* Social */}
<h2 className="grow text-base font-semibold text-slate-800 truncate mb-2.5 mt-8">Social 🖋</h2> <div className={`${sosialData.length > 0 || 'hidden'}`}>
<div className="overflow-x-auto w-[828px]"> <h2 className="grow text-base font-semibold text-slate-800 truncate mb-2.5 mt-8">Social 🖋</h2>
<table className="w-[828px] table-auto w-full"> <div className="overflow-x-auto w-[828px]">
<tbody className="text-sm divide-slate-200 divide-y "> <table className="w-[828px] table-auto w-full">
{userData.filter(data => data.category === 'Social') <tbody className="text-sm divide-slate-200 divide-y ">
.map(data => { {sosialData.map(data => {
return ( return (
<ProfileTableItem <ProfileTableItem
key={data.id} key={data.label}
id={data.id} id={data.label}
image={data.image} image={defaultData.image}
value={data.value} value={data.value.charAt(0).toUpperCase()+data.value.slice(1)}
property={data.property} property={data.label}
status={data.status} status={defaultData.status}
transactions={data.transactions} transactions={defaultData.transactions}
avatars={data.avatars} avatars={defaultData.avatars}
handleClick={handleClick} handleClick={handleClick}
isChecked={isCheck.includes(data.id)} isChecked={isCheck.includes(data.label)}
/> />
) )
}) })}
} </tbody>
</tbody> </table>
</table> </div>
</div>
{/* Other */}
<div className={`${otherData.length > 0 || 'hidden'}`}>
<h2 className="grow text-base font-semibold text-slate-800 truncate mb-2.5 mt-8">Other 🖋</h2>
<div className="overflow-x-auto w-[828px]">
<table className="w-[828px] table-auto w-full">
<tbody className="text-sm divide-slate-200 divide-y ">
{otherData.map(data => {
return (
<ProfileTableItem
key={data.label}
id={data.label}
image={defaultData.image}
value={data.value}
property={data.label}
status={defaultData.status}
transactions={defaultData.transactions}
avatars={defaultData.avatars}
handleClick={handleClick}
isChecked={isCheck.includes(data.label)}
/>
)
})}
</tbody>
</table>
</div>
</div> </div>
</> </>
) )

103
src/shared/fetchWrapper.js Normal file
View File

@@ -0,0 +1,103 @@
import {registrationStore} from '../store/store';
function prepareUrl(url) {
if (url.startsWith('http')) return url;
return `http://3.125.47.101/api/${url}`;
}
function handleRequest(method, url, headers, attempts, token, body) {
return new Promise((resolve, reject) => {
(function internalRequest() {
return request(method, url, headers, token, body)
.then(resolve)
.catch(err => --attempts > 0 ? internalRequest() : reject(err))
})()
})
.then((res) => res.json())
.catch(() => [])
};
/**
* Request
* @param {string} method - method name
* @param {string} url - endpoint url.
* @param {string} token - auth token
* @param {object} body - request body.
* @param {object} headers - request headers
*/
function request(method, url, headers = {}, token, body) {
let controller = new AbortController;
let requestOptions = {};
if (method === 'GET' || method === 'DELETE'){
requestOptions = {
method: method,
headers: {
Authorization: `IDNTTY ${token}`,
...headers
},
signal: controller.signal,
}
};
if (method === 'POST' || method === 'PUT'){
requestOptions = {
method: method,
headers: {
Authorization: `IDNTTY ${token}`,
Accept: 'application/json',
'Content-Type': 'application/json',
...headers
},
body: JSON.stringify(body),
signal: controller.signal,
}
};
if (!token) {
requestOptions.headers.Authorization = null;
}
setTimeout(() => controller.abort(), 3000);
return fetch(prepareUrl(url), requestOptions);
};
function get(url, headers, attempts = 1) {
return handleRequest('GET', url, headers, attempts, null);
}
function getAuth(url, headers, attempts = 1) {
return handleRequest('GET', url, headers, attempts, registrationStore.tokenKey);
}
function del(url, headers, attempts = 1) {
return handleRequest('DELETE', url, headers, attempts, null);
}
function delAuth(url, headers, attempts = 1) {
return handleRequest('DELETE', url, headers, attempts, registrationStore.tokenKey);
}
function post(url, headers, body, attempts = 1) {
return handleRequest('POST', url, headers, attempts, null, body);
}
function postAuth(url, headers, body, attempts = 1) {
return handleRequest('POST', url, headers, attempts, registrationStore.tokenKey, body);
}
function put(url, headers, body, attempts = 1) {
return handleRequest('PUT', url, headers, attempts, null, body);
}
function putAuth(url, headers, body, attempts = 1) {
return handleRequest('PUT', url, headers, attempts, registrationStore.tokenKey, body);
}
export const fetchWrapper = {
get,
del,
post,
put,
getAuth,
delAuth,
postAuth,
putAuth,
baseUrl: ''
};

108
src/store/store.js Normal file
View File

@@ -0,0 +1,108 @@
import { reaction, makeAutoObservable } from "mobx";
import {cryptography} from "@liskhq/lisk-client";
import {getNodeInfo} from '../api/node';
import {fetchWrapper} from '../shared/fetchWrapper';
class Store {
_accountData = {}
_passPhrase='rocket north inform swift improve fringe sweet crew client canyon bean autumn'
_nodeInfo = {}
_userData = {}
constructor() {
makeAutoObservable(this);
this.fetchNodeInfo()
reaction(() => this.tokenKey, () => fetchWrapper.getAuth('http://3.125.47.101/api/data/account', {
networkIdentifier: this.nodeInfo.networkIdentifier,
lastBlockID: this.nodeInfo.lastBlockID
}).then((res) => this.userDataFetchChange(res)))
};
fetchCreateNewAccount() {
fetchWrapper.postAuth('http://3.125.47.101/api/data/account', {
networkIdentifier: this.nodeInfo.networkIdentifier,
lastBlockID: this.nodeInfo.lastBlockID
}, [...this.encryptAccountData]).catch(()=>{})
};
userDataFetchChange(res) {
this._userData = res;
}
savePassPhrase(phrase) {
this._passPhrase = phrase
}
saveDataRegistration(data) {
this._accountData = data;
};
fetchNodeInfo() {
getNodeInfo()
.then((info) => this.fetchNodeInfoSuccess(info))
.catch((err) => this.fetchNodeInfoFailed(err))
}
fetchNodeInfoSuccess(info) {
this._nodeInfo = info.data;
}
fetchNodeInfoFailed(err) {
console.log(err)
}
get userData() {
return this._userData;
}
get passPhrase() {
return 'rocket north inform swift improve fringe sweet crew client canyon bean autumn'
}
get accountData() {
return this._accountData
};
get encryptAccountData() {
return Object.keys(this.accountData).map((label) => {
let value = cryptography.encryptMessageWithPassphrase(cryptography.getRandomBytes(32).toString('hex') + ":" + this.accountData[label], this.passPhrase, this.pubKey);
return {
"label": label,
"value": value.encryptedMessage,
"value_nonce": value.nonce,
}
})
}
get nodeInfo() {
return this._nodeInfo;
}
get addressAndPubKey() {
return cryptography.getAddressAndPublicKeyFromPassphrase(this.passPhrase);
}
get pubKey() {
return this.addressAndPubKey.publicKey.toString('hex');
}
get address() {
return cryptography.bufferToHex(this.addressAndPubKey.address)
}
get tokenKey() {
const stringToSign = this.nodeInfo.networkIdentifier + this.nodeInfo.lastBlockID;
if(!stringToSign)
return '';
const sign = cryptography.signDataWithPassphrase(Buffer.from(stringToSign, 'hex'), this.passPhrase).toString('hex')
return this.pubKey + ':' +sign
}
};
export const registrationStore = new Store();

View File

@@ -0,0 +1,53 @@
import { cryptography } from "@liskhq/lisk-client";
import { makeAutoObservable, reaction } from "mobx";
import { registrationStore } from './store';
const labelMap = {
first_name: 'First name',
second_name: 'Second name',
gender: "Gender",
birthdate: "Birthdate",
place_of_birth: "Place of birth",
nationality: "Nationality",
national_id: "National id",
national_doctype: "National doctype",
national_doc_id: "National doc id",
national_doc_issue_date: "National doc issue date",
national_doc_expiry_date: "National doc expiry date",
telephone: "Telephone",
twitter: "Twitter",
facebook: "Facebook",
instagram: "Instagram",
youtube: "Youtube",
wechat: "Wechat",
tiktok: "Tiktok",
linkedin: "Linkedin",
vk: "Vk",
github: "Github",
telegram: "Telegram",
qq: "Qq",
snapchat: "Snapchat",
reddit: "Reddit"
};
class UserDataStore{
_data = [];
constructor() {
makeAutoObservable(this);
reaction(() => ({data: registrationStore.userData}), ({data}) => {
this._data = data.data;
})
}
get decryptedData(){
return this._data.map((elem) => ({
...elem,
label: labelMap[elem.label],
value: cryptography.decryptMessageWithPassphrase(elem.value, elem.value_nonce, registrationStore.passPhrase, registrationStore.pubKey).split(':')[1]
}))
}
}
export const userDataStore = new UserDataStore();

View File

@@ -2,7 +2,7 @@ import resolveConfig from 'tailwindcss/resolveConfig';
export const tailwindConfig = () => { export const tailwindConfig = () => {
// Tailwind config // Tailwind config
return resolveConfig('./src/css/tailwind.config.js') return resolveConfig('./src/tailwind.config.js')
} }
export const hexToRGB = (h) => { export const hexToRGB = (h) => {

View File

@@ -1,29 +0,0 @@
import { defineConfig } from 'vite'
import postcss from './postcss.config.js'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
define: {
'process.env': process.env
},
css: {
postcss,
},
plugins: [react()],
resolve: {
alias: [
{
find: /^~.+/,
replacement: (val) => {
return val.replace(/^~/, "");
},
},
],
},
build: {
commonjsOptions: {
transformMixedEsModules: true,
}
}
})

91
webpack.config.js Normal file
View File

@@ -0,0 +1,91 @@
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const plugins = [
new HtmlWebpackPlugin({
favicon: './public/favicon.ico',
template: './public/index.html',
}),
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}),
new webpack.ProvidePlugin({
process: 'process/browser',
}),
require('tailwindcss')
];
module.exports = {
entry: './src/main.jsx',
mode: 'development',
devtool: 'eval-source-map',
plugins,
output: {
clean: true,
path: path.resolve(__dirname, 'build'),
assetModuleFilename: 'assets/[hash][ext]',
filename: 'main.[hash:8].js',
},
devServer: {
open: true,
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 'auto',
hot: true,
client: {
progress: true,
},
},
module: {
rules: [
{ test: /\.(html)$/, use: ['html-loader'] },
{
test: /\.m?jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.scss$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'postcss-loader'
},
{
loader: 'sass-loader'
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ["file-loader"]
}
]
},
resolve: {
extensions: ['.js', '.jsx'],
fallback: {
"path": require.resolve("path-browserify"),
"os": require.resolve("os-browserify/browser"),
"querystring": require.resolve("querystring-es3"),
"crypto": require.resolve("crypto-browserify"),
"util": require.resolve("util/"),
"stream": require.resolve("stream-browserify"),
"url": require.resolve("url/"),
"buffer": require.resolve("buffer/"),
"fs": false
}
},
};