Compare commits
33 Commits
Deleting_e
...
DID-5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d4f27660a | ||
|
|
9703371f28 | ||
|
|
16ca8a1df2 | ||
|
|
e883b190a7 | ||
|
|
85897a8efe | ||
|
|
5f85d1696a | ||
|
|
1c539e5255 | ||
|
|
bcfc018560 | ||
|
|
e175ca0295 | ||
|
|
bac246eac2 | ||
|
|
f65c99e86a | ||
|
|
63439b2da1 | ||
|
|
1d9368d200 | ||
|
|
3a4ed9daf9 | ||
|
|
54ccd3ed9f | ||
|
|
fe1105e9d6 | ||
|
|
95cc2ac1e0 | ||
|
|
b4516ef18e | ||
|
|
d617de75b5 | ||
|
|
83e5e39d3c | ||
|
|
f89d59e7df | ||
|
|
c944644db0 | ||
|
|
bfdddee46a | ||
|
|
0da68ddd63 | ||
|
|
f209ee5f5f | ||
|
|
9bef3d9311 | ||
|
|
632b98a81a | ||
|
|
eeaabeebf3 | ||
|
|
81f5ec2741 | ||
|
|
3fcda54070 | ||
|
|
49aff14a80 | ||
|
|
da04efbf80 | ||
|
|
541c5988e9 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
# dependencies
|
||||
/node_modules
|
||||
.idea
|
||||
26
README.md
26
README.md
@@ -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/].
|
||||
21
index.html
21
index.html
@@ -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>
|
||||
13275
package-lock.json
generated
13275
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
49
package.json
49
package.json
@@ -2,27 +2,60 @@
|
||||
"name": "mosaic-react",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
"dev": "webpack-dev-server",
|
||||
"build": "webpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@liskhq/lisk-client": "^5.2.2",
|
||||
"@tailwindcss/forms": "^0.4.0",
|
||||
"autoprefixer": "^10.4.5",
|
||||
"buffer": "^6.0.3",
|
||||
"chart.js": "^3.5.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",
|
||||
"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-dom": "^17.0.2",
|
||||
"react-flatpickr": "^3.10.7",
|
||||
"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": {
|
||||
"@vitejs/plugin-react": "^1.0.7",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"@babel/core": "^7.18.5",
|
||||
"@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",
|
||||
"sass": "^1.49.0",
|
||||
"sass": "^1.52.3",
|
||||
"sass-loader": "^13.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import tailwind from 'tailwindcss'
|
||||
import autoprefixer from 'autoprefixer'
|
||||
import tailwindConfig from './src/css/tailwind.config.js'
|
||||
const tailwind = require('tailwindcss')
|
||||
const autoprefixer = require('autoprefixer')
|
||||
const tailwindConfig = require('./src/tailwind.config.js')
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
plugins: [tailwind(tailwindConfig), autoprefixer],
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
11
public/index.html
Normal file
11
public/index.html
Normal 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>
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
|
||||
import {
|
||||
Routes,
|
||||
Route,
|
||||
useLocation
|
||||
useLocation,
|
||||
} from 'react-router-dom';
|
||||
|
||||
import './css/style.scss';
|
||||
|
||||
3
src/api/node.js
Normal file
3
src/api/node.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import {fetchWrapper} from '../shared/fetchWrapper';
|
||||
|
||||
export const getNodeInfo = (headers) => fetchWrapper.get('node/info/', headers)
|
||||
@@ -1,12 +1,14 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import {observer} from "mobx-react-lite";
|
||||
import { Link } from 'react-router-dom';
|
||||
import Transition from '../utils/Transition';
|
||||
|
||||
import {registrationStore} from "../store/store";
|
||||
import {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg";
|
||||
import UserAvatar from '../images/user-avatar-32.png';
|
||||
|
||||
function DropdownProfile({
|
||||
const DropdownProfile = observer(({
|
||||
align
|
||||
}) {
|
||||
})=>{
|
||||
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
|
||||
@@ -43,7 +45,7 @@ function DropdownProfile({
|
||||
onClick={() => setDropdownOpen(!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">
|
||||
<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">
|
||||
@@ -95,6 +97,6 @@ function DropdownProfile({
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default DropdownProfile;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
|
||||
// Additional styles
|
||||
@import 'additional-styles/utility-patterns.scss';
|
||||
@import 'additional-styles/range-slider.scss';
|
||||
@import 'additional-styles/toggle-switch.scss';
|
||||
|
||||
@@ -36,12 +36,9 @@ function createBlob(options) {
|
||||
}
|
||||
|
||||
export var generateSvgAvatar = ( function () {
|
||||
return function (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);
|
||||
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);
|
||||
//pubKey = "73f47d4929d7b2b6598ca4999d52567f9bb5aef3e353e0a66b468096db6e4e88"
|
||||
if (!localStorage.getItem('svgKey')) {
|
||||
localStorage.setItem('svgKey', `${pubKey}`)
|
||||
}
|
||||
let particles = [];
|
||||
let gradients = ['#000000'];
|
||||
let defs = [];
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import React from 'react'
|
||||
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'
|
||||
|
||||
globalThis.Buffer = Buffer
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Router>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import React, {useState} from 'react';
|
||||
|
||||
import Sidebar from '../partials/Sidebar';
|
||||
import Header from '../partials/Header';
|
||||
@@ -19,9 +19,6 @@ import DashboardCard10 from '../partials/dashboard/DashboardCard10';
|
||||
import DashboardCard11 from '../partials/dashboard/DashboardCard11';
|
||||
|
||||
function Dashboard() {
|
||||
useEffect(() => {
|
||||
localStorage.setItem('svgKey', '')
|
||||
}, [])
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import React from 'react';
|
||||
import React, {useEffect} from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import OnboardingImage from '../images/onboarding-image.jpg';
|
||||
import OnboardingDecoration from '../images/auth-decoration.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 (
|
||||
<main className="bg-white">
|
||||
|
||||
@@ -127,6 +136,6 @@ function Onboarding1() {
|
||||
|
||||
</main>
|
||||
);
|
||||
}
|
||||
})
|
||||
|
||||
export default Onboarding1;
|
||||
|
||||
@@ -1,11 +1,54 @@
|
||||
import React from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
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 OnboardingDecoration from '../images/auth-decoration.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 (
|
||||
<main className="bg-white">
|
||||
|
||||
@@ -60,23 +103,24 @@ function Onboarding2() {
|
||||
<form>
|
||||
<div className="space-y-4 mb-8">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1" htmlFor="email">First name <span className="text-rose-500">*</span></label>
|
||||
<input id="email" className="form-input w-full" type="email" />
|
||||
<label className="block text-sm font-medium mb-1" htmlFor="first_name">First name <span className="text-rose-500">*</span></label>
|
||||
<input id="first_name" onChange={(event)=>saveValueChange(event)} className="form-input w-full" type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1" htmlFor="name">Last name <span className="text-rose-500">*</span></label>
|
||||
<input id="name" className="form-input w-full" type="text" />
|
||||
<label className="block text-sm font-medium mb-1" htmlFor="second_name">Last name <span className="text-rose-500">*</span></label>
|
||||
<input id="second_name" onChange={(event)=>saveValueChange(event)} className="form-input w-full" type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1" htmlFor="role">Gender <span className="text-rose-500">*</span></label>
|
||||
<select id="role" className="form-select w-full">
|
||||
<label 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">
|
||||
<option className="hidden"></option>
|
||||
<option>male</option>
|
||||
<option>feemale</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1" htmlFor="password">Date of birth <span className="text-rose-500">*</span></label>
|
||||
<input id="password" className="form-input w-full" type="password" autoComplete="on" />
|
||||
<label 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" />
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</label>
|
||||
</div>
|
||||
<button className="btn bg-indigo-500 hover:bg-indigo-600 text-white ml-3 whitespace-nowrap">
|
||||
<Link to="/onboarding-3">Sign Up</Link>
|
||||
<button onClick={()=>saveStoreRegistration()} className="btn bg-indigo-500 hover:bg-indigo-600 text-white ml-3 whitespace-nowrap">
|
||||
<Link id="signup" to={fillingForm && "/onboarding-3"} >Sign Up</Link>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -114,6 +158,6 @@ function Onboarding2() {
|
||||
|
||||
</main>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default Onboarding2;
|
||||
|
||||
@@ -1,17 +1,34 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import React, { useState} from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg";
|
||||
import Logo from "../images/logo.png";
|
||||
import {registrationStore} from "../store/store";
|
||||
import { passphrase } from "@liskhq/lisk-client";
|
||||
|
||||
function Onboarding3() {
|
||||
|
||||
useEffect(()=>{
|
||||
return ()=>{
|
||||
localStorage.setItem('svgAvatar', generateSvgAvatar())
|
||||
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)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const arrayBadges = ['judge', 'jelly', 'wasp', 'true', 'clog', 'forward', 'talent', 'ozone', 'belive', 'fresh', 'bulk', 'hobby']
|
||||
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 (
|
||||
<main className="bg-white">
|
||||
|
||||
@@ -66,21 +83,21 @@ function Onboarding3() {
|
||||
<h1 className="text-3xl text-slate-800 font-bold mb-12">Company information ✨</h1>
|
||||
{/* htmlForm */}
|
||||
<div className="grid grid-cols-4 gap-y-2.5 gap-[18px] mb-14">
|
||||
{arrayBadges.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">
|
||||
<span className="text-sm font-medium">{tag}</span>
|
||||
{passPhrase.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">
|
||||
<span className="text-sm font-medium">{tag.str}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<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">
|
||||
<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>
|
||||
<span className="ml-2">Paste</span>
|
||||
</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">
|
||||
<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"/>
|
||||
@@ -106,7 +123,7 @@ function Onboarding3() {
|
||||
{/* 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 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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {generateSvgAvatar} from "../images/GenerateOnboardingSvg/GenerateSvg";
|
||||
import Logo from "../images/logo.png";
|
||||
import {observer} from "mobx-react-lite";
|
||||
import { registrationStore } from "../store/store";
|
||||
|
||||
function Onboarding4() {
|
||||
const Onboarding4 = observer(()=>{
|
||||
|
||||
const [checkBoxesSelected, setCheckBoxesSelected] = useState(false)
|
||||
|
||||
function createAccount () {
|
||||
let checkBoxes=[...document.getElementsByClassName("form-checkbox")]
|
||||
if(checkBoxes.find(item=>item.checked===false)) {
|
||||
return
|
||||
} else {
|
||||
registrationStore.fetchCreateNewAccount()
|
||||
setCheckBoxesSelected(true)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
return ()=>{
|
||||
localStorage.setItem('svgAvatar', generateSvgAvatar())
|
||||
if(checkBoxesSelected===true) {
|
||||
document.getElementById("link-dashboard").click()
|
||||
}
|
||||
}, [])
|
||||
},[checkBoxesSelected])
|
||||
|
||||
return (
|
||||
<main className="bg-white">
|
||||
@@ -66,9 +81,9 @@ function Onboarding4() {
|
||||
<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>
|
||||
<h1 className="text-3xl text-slate-800 font-bold mb-8">Nice to meet you, Ivan 🙌</h1>
|
||||
<button className="btn px-6 bg-indigo-500 hover:bg-indigo-600 text-white">
|
||||
<Link to="/dashboard">Go To Profile -></Link>
|
||||
<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={()=>createAccount()} className="btn px-6 bg-indigo-500 hover:bg-indigo-600 text-white">
|
||||
<Link id="link-dashboard" to={checkBoxesSelected && "/dashboard"}>Go To Profile -></Link>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -97,7 +112,7 @@ function Onboarding4() {
|
||||
{/* 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 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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -106,6 +121,6 @@ function Onboarding4() {
|
||||
|
||||
</main>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default Onboarding4;
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import React, { useState } from 'react';
|
||||
import { userDataStore } from '../../store/userDataStore';
|
||||
import Sidebar from '../../partials/Sidebar';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import Header from '../../partials/Header';
|
||||
import ProfileTable from '../../partials/digitalId/ProfileTable';
|
||||
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 [buttonPanelOpen, setButtonPanelOpen] = useState(true);
|
||||
const [removePanelOpen, setRemovePanelOpen] = useState(false);
|
||||
@@ -17,6 +15,34 @@ function Profile () {
|
||||
const [addPanelOpen, setAddPanelOpen] = useState(false);
|
||||
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) => {
|
||||
setSelectedItems([...selectedItems]);
|
||||
if (selectedItems.length > 0) {
|
||||
@@ -39,7 +65,16 @@ function Profile () {
|
||||
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 = () => {
|
||||
setCurrentValues(defaultValues);
|
||||
setAddPanelOpen(false);
|
||||
setButtonPanelOpen(true);
|
||||
}
|
||||
@@ -54,140 +89,14 @@ function Profile () {
|
||||
setButtonPanelOpen(true);
|
||||
}
|
||||
|
||||
const userData = [
|
||||
{
|
||||
id: '0',
|
||||
category: 'General',
|
||||
validated: true,
|
||||
blockchained: true,
|
||||
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]
|
||||
},
|
||||
]
|
||||
const handleInput = (text, field) => {
|
||||
changeCurrentValues(text, field);
|
||||
setPropertyValues(initialPropertyValues);
|
||||
if (text) {
|
||||
let reg = new RegExp(`^${text}`, 'img');
|
||||
setPropertyValues((prevState) => prevState.filter((element) => element.match(reg)));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen overflow-hidden">
|
||||
@@ -222,7 +131,7 @@ function Profile () {
|
||||
</ul>
|
||||
</div>
|
||||
{/* Table */}
|
||||
<ProfileTable selectedItems={handleSelectedItems} userData={userData}/>
|
||||
<ProfileTable selectedItems={handleSelectedItems} userData={userDataStore.decryptedData}/>
|
||||
</div>
|
||||
{/* Left sidebar */}
|
||||
<div>
|
||||
@@ -246,22 +155,56 @@ function Profile () {
|
||||
<div className="flex flex-col gap-y-3">
|
||||
<div>
|
||||
<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>
|
||||
<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>
|
||||
<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 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>
|
||||
<div className="flex items-center">
|
||||
<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">
|
||||
<span className="bg-white shadow-sm" aria-hidden="true"></span>
|
||||
</label>
|
||||
@@ -278,7 +221,7 @@ function Profile () {
|
||||
</button>
|
||||
<button
|
||||
className="btn-sm bg-indigo-500 hover:bg-indigo-600 text-white"
|
||||
onClick={cancelAddPanel}
|
||||
onClick={sendValues}
|
||||
>
|
||||
Send
|
||||
</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`}>
|
||||
<h2 className="grow text-base font-semibold text-slate-800 truncate mb-2">Summary</h2>
|
||||
<div className="flex flex-col">
|
||||
{userData.filter(({ id }) => selectedItems.includes(id)).map(item => (
|
||||
<span key={item.id} className="text-sm font-normal text-slate-600 py-3 border-b border-slate-200">{item.property}</span>
|
||||
{userDataStore.decryptedData.filter(({ label }) => selectedItems.includes(label)).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 className="flex flex-row justify-between pt-[16px] pb-7 items-center">
|
||||
@@ -390,6 +333,6 @@ function Profile () {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default Profile;
|
||||
export default Profile
|
||||
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import Sidebar from '../../partials/Sidebar';
|
||||
import Header from '../../partials/Header';
|
||||
|
||||
function Verify () {
|
||||
const Verify = (() => {
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [descriptionOpen, setDescriptionOpen] = useState(false);
|
||||
|
||||
@@ -165,6 +165,6 @@ function Verify () {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default Verify;
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import ProfileTableItem from './ProfileTableItem';
|
||||
import ProfileIcon from '../../images/profile-icon.svg';
|
||||
|
||||
function ProfileTable({
|
||||
selectedItems,
|
||||
@@ -8,6 +9,13 @@ function ProfileTable({
|
||||
|
||||
const [isCheck, setIsCheck] = useState([]);
|
||||
|
||||
const defaultData = {
|
||||
image: ProfileIcon,
|
||||
status: 'Stored',
|
||||
transactions: ['0x7234ABC342342352345', '0x5745DEF342342352345'],
|
||||
avatars: null
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
selectedItems(isCheck);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -21,86 +29,124 @@ 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 (
|
||||
<>
|
||||
{/* General */}
|
||||
<div className={`${generalData.length > 0 || 'hidden'}`}>
|
||||
<h2 className="grow text-base font-semibold text-slate-800 truncate mb-2.5 mt-8">General 🖋️</h2>
|
||||
<div className="overflow-x-auto w-[828px]">
|
||||
<table className="w-[828px] table-auto w-full">
|
||||
<tbody className="divide-slate-200 divide-y">
|
||||
{userData.filter(data => data.category === 'General')
|
||||
.map(data => {
|
||||
{generalData.map((data) => {
|
||||
return (
|
||||
<ProfileTableItem
|
||||
key={data.id}
|
||||
id={data.id}
|
||||
image={data.image}
|
||||
value={data.value}
|
||||
property={data.property}
|
||||
status={data.status}
|
||||
transactions={data.transactions}
|
||||
avatars={data.avatars}
|
||||
key={data.label}
|
||||
id={data.label}
|
||||
image={defaultData.image}
|
||||
value={data.value.charAt(0).toUpperCase()+data.value.slice(1)}
|
||||
property={data.label}
|
||||
status={defaultData.status}
|
||||
transactions={defaultData.transactions}
|
||||
avatars={defaultData.avatars}
|
||||
handleClick={handleClick}
|
||||
isChecked={isCheck.includes(data.id)}
|
||||
isChecked={isCheck.includes(data.label)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/* Nationality */}
|
||||
<div className={`${nationalityData.length > 0 || 'hidden'}`}>
|
||||
<h2 className="grow text-base font-semibold text-slate-800 truncate mb-2.5 mt-8">Nationality 🖋️</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 ">
|
||||
{userData.filter(data => data.category === 'Nationality')
|
||||
.map(data => {
|
||||
{nationalityData.map(data => {
|
||||
return (
|
||||
<ProfileTableItem
|
||||
key={data.id}
|
||||
id={data.id}
|
||||
image={data.image}
|
||||
value={data.value}
|
||||
property={data.property}
|
||||
status={data.status}
|
||||
transactions={data.transactions}
|
||||
avatars={data.avatars}
|
||||
key={data.label}
|
||||
id={data.label}
|
||||
image={defaultData.image}
|
||||
value={data.value.charAt(0).toUpperCase()+data.value.slice(1)}
|
||||
property={data.label}
|
||||
status={defaultData.status}
|
||||
transactions={defaultData.transactions}
|
||||
avatars={defaultData.avatars}
|
||||
handleClick={handleClick}
|
||||
isChecked={isCheck.includes(data.id)}
|
||||
isChecked={isCheck.includes(data.label)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/* Social */}
|
||||
<div className={`${sosialData.length > 0 || 'hidden'}`}>
|
||||
<h2 className="grow text-base font-semibold text-slate-800 truncate mb-2.5 mt-8">Social 🖋️</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 ">
|
||||
{userData.filter(data => data.category === 'Social')
|
||||
.map(data => {
|
||||
{sosialData.map(data => {
|
||||
return (
|
||||
<ProfileTableItem
|
||||
key={data.id}
|
||||
id={data.id}
|
||||
image={data.image}
|
||||
value={data.value}
|
||||
property={data.property}
|
||||
status={data.status}
|
||||
transactions={data.transactions}
|
||||
avatars={data.avatars}
|
||||
key={data.label}
|
||||
id={data.label}
|
||||
image={defaultData.image}
|
||||
value={data.value.charAt(0).toUpperCase()+data.value.slice(1)}
|
||||
property={data.label}
|
||||
status={defaultData.status}
|
||||
transactions={defaultData.transactions}
|
||||
avatars={defaultData.avatars}
|
||||
handleClick={handleClick}
|
||||
isChecked={isCheck.includes(data.id)}
|
||||
isChecked={isCheck.includes(data.label)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
})}
|
||||
</tbody>
|
||||
</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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
103
src/shared/fetchWrapper.js
Normal file
103
src/shared/fetchWrapper.js
Normal 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
108
src/store/store.js
Normal 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();
|
||||
53
src/store/userDataStore.js
Normal file
53
src/store/userDataStore.js
Normal 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();
|
||||
@@ -2,7 +2,7 @@ import resolveConfig from 'tailwindcss/resolveConfig';
|
||||
|
||||
export const tailwindConfig = () => {
|
||||
// Tailwind config
|
||||
return resolveConfig('./src/css/tailwind.config.js')
|
||||
return resolveConfig('./src/tailwind.config.js')
|
||||
}
|
||||
|
||||
export const hexToRGB = (h) => {
|
||||
|
||||
@@ -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
91
webpack.config.js
Normal 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
|
||||
}
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user