fix process list page and add process page

This commit is contained in:
AnisHADJARAB 2024-10-30 10:34:02 +00:00
parent 29f09e92b7
commit d7da806989
7 changed files with 324 additions and 115 deletions

View File

@ -714,4 +714,62 @@ select[data-multi-select-plugin] {
.autocomplete-list {
max-height: 130px;
overflow-y: auto;
}
/**************************************** Process page card ******************************************************/
.process-card {
min-width: 300px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
min-height: 40vh;
max-height: 60vh;
justify-content: space-between;
padding: 1rem;
overflow-y: auto;
}
.process-card-content {
text-align: left;
font-size: .8em;
position: relative;
left: 2vw;
width: 90%;
.process-title {
font-weight: bold;
padding: 1rem 0;
}
.process-element {
padding: .4rem 0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
&.selected {
background-color: rgba(26, 28, 24, .08);
}
}
.selected-process-zone {
background-color: rgba(26, 28, 24, .08);
}
}
.process-card-description {
padding: 20px;
font-size: 1em;
color: #333;
width: 90%;
}
.process-card-action {
width: 100%;
}

View File

@ -0,0 +1,35 @@
<div class="nav-wrapper">
<div></div>
<div class="brand-logo">4NK</div>
<div class="nav-right-icons">
<div class="notification-container">
<div class="bell-icon">
<svg class="notification-bell" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path d="M224 0c-17.7 0-32 14.3-32 32V51.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416H424c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6C399.5 322.9 384 278.8 384 233.4V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32zm0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3C98.1 328 112 281.3 112 233.4V208c0-61.9 50.1-112 112-112zm64 352H224 160c0 17 6.7 33.3 18.7 45.3s28.3 18.7 45.3 18.7s33.3-6.7 45.3-18.7s18.7-28.3 18.7-45.3z"/></svg>
</div>
<div class="notification-badge">1</div>
</div>
<div class="burger-menu">
<svg class="burger-menu" onclick="toggleMenu()" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/>
</svg>
<div class="menu-content" id="menu">
<a href="#">Revoke</a>
<a href="#">Export</a>
<a href="#">Import</a>
<a href="#">Disconnect</a>
</div>
</div>
</div>
</div>
<div class="title-container">
<h1>Process {{processTitle}}</h1>
</div>
<div class="process-container">
</div>

View File

@ -0,0 +1,55 @@
import Services from "../../services/service"
let currentPageStyle: HTMLStyleElement | null = null;
export async function initProcessElement(id: string, zone: string) {
console.log("🚀 ~ init ~ id:", id)
const processes = await getProcesses()
const currentProcess = processes.find(process => process[0] === id)[1]
console.log("🚀 ~ init ~ currentProcess:", currentProcess)
const test = await loadPage({processTitle: currentProcess.title, inputValue: 'Hello World !'})
console.log("🚀 ~ initProcessElement ~ test:", currentProcess.html)
const wrapper = document.querySelector('.process-container')
if(wrapper) {
wrapper.innerHTML = interpolate(currentProcess.html, {processTitle: currentProcess.title, inputValue: 'Hello World !'})
injectCss(currentProcess.css)
}
}
function interpolate(template: string, data: { [key: string]: string }) {
return template.replace(/{{(.*?)}}/g, (_, key) => data[key.trim()]);
}
async function loadPage(data?: any) {
const content = document.getElementById('containerId');
if (content && data) {
if (data) {
content.innerHTML = interpolate(content.innerHTML, data);
}
}
}
function injectCss(cssContent: string) {
removeCss(); // Ensure that the previous CSS is removed
currentPageStyle = document.createElement('style');
currentPageStyle.type = 'text/css';
currentPageStyle.appendChild(document.createTextNode(cssContent));
document.head.appendChild(currentPageStyle);
}
function removeCss() {
if (currentPageStyle) {
document.head.removeChild(currentPageStyle);
currentPageStyle = null;
}
}
async function getProcesses(): Promise<any[]> {
const service = await Services.getInstance();
const processes = await service.getProcesses()
console.log("🚀 ~ Services ~ getProcesses ~ processes:", processes)
return processes
}

View File

@ -31,19 +31,19 @@
</div>
<div class="process-container">
<div class="card">
<div class="card-description">
<div class="process-card">
<div class="process-card-description">
<div class="input-container">
<select multiple data-multi-select-plugin id="autocomplete" placeholder="Filter processes..." class="select-field">
</select>
<label for="autocomplete" class="input-label">Filter processes :</label>
<div class="selected-processes"></div>
</div>
<div class="card-content">
<div class="process-card-content">
</div>
</div>
<div class="card-action">
<a class="btn">OK</a>
<div class="process-card-action">
<a class="btn" onclick="goToProcessPage()">OK</a>
</div>
</div>

View File

@ -1,3 +1,4 @@
import { navigate } from '../../router';
import Services from '../../services/service';
import { IProcess } from '~/models/process.model';
function toggleMenu() {
@ -10,7 +11,8 @@ function toggleMenu() {
}
// Initialize function, create initial tokens with itens that are already selected by the user
function init(element: HTMLElement) {
export async function init() {
const element = document.querySelector('select') as HTMLSelectElement;
// Create div that wroaps all the elements inside (select, elements selected, search div) to put select inside
const wrapper = document.createElement('div');
wrapper.addEventListener('click', clickOnWrapper);
@ -38,6 +40,14 @@ function init(element: HTMLElement) {
search_div.appendChild(autocomplete_list);
search_div.appendChild(dropdown_icon);
const processes = await getProcesses();
for(let process of processes) {
const processName = process[1].title;
const opt = new Option(processName)
opt.value = processName
element.add(opt)
}
// set the wrapper as child (instead of the element)
element.parentNode?.replaceChild(wrapper, element);
// set element as child of wrapper
@ -127,7 +137,6 @@ function createToken(wrapper: HTMLElement, value: any) {
close.setAttribute('tabindex', '-1');
close.setAttribute('data-option', value);
close.setAttribute('data-hits', '0');
close.setAttribute('href', '#');
close.innerText = 'x';
close.addEventListener('click', removeToken);
token.appendChild(token_span);
@ -144,6 +153,7 @@ function clickDropdown(e: Event) {
dropdown.classList.toggle('active');
if (dropdown.classList.contains('active')) {
removePlaceholder(wrapper as HTMLElement);
input_search?.focus();
@ -169,6 +179,7 @@ function clearAutocompleteList(select: HTMLSelectElement) {
// Populate the autocomplete list following a given query from the user
function populateAutocompleteList(select: HTMLSelectElement, query: string, dropdown = false) {
const { autocomplete_options } = getOptions(select);
console.log("🚀 ~ populateAutocompleteList ~ autocomplete_options:", autocomplete_options)
let options_to_show;
@ -209,17 +220,19 @@ function populateAutocompleteList(select: HTMLSelectElement, query: string, drop
// Listener to autocomplete results when clicked set the selected property in the select option
function selectOption(e: any) {
console.log("🚀 ~ selectOption ~ e:", e)
const wrapper = e.target.parentNode.parentNode.parentNode;
const input_search = wrapper.querySelector('.selected-input');
const option = wrapper.querySelector(`select option[value="${e.target.dataset.value}"]`);
console.log("🚀 ~ selectOption ~ option:", option)
option.setAttribute('selected', '');
createToken(wrapper, e.target.dataset.value);
if (input_search.value) {
input_search.value = '';
}
// showSelectedProcess(e.target.dataset.value);
showSelectedProcess(e.target.dataset.value);
input_search.focus();
@ -331,14 +344,6 @@ function deletePressed(e: KeyboardEvent) {
// opt.innerHTML = text;
// select.appendChild(opt);
// }
// get select that has the options available
const select = document.querySelectorAll('[data-multi-select-plugin]') as NodeListOf<HTMLElement>;
select.forEach((select) => {
console.log(select);
init(select);
});
// Dismiss on outside click
document.addEventListener('click', () => {
// get select that has the options available
@ -367,29 +372,31 @@ export async function unpair() {
(window as any).unpair = unpair;
async function showSelectedProcess(event: MouseEvent) {
const elem = event.target;
async function showSelectedProcess(elem: MouseEvent) {
if (elem) {
const cardContent = document.querySelector('.card-content');
const cardContent = document.querySelector('.process-card-content');
const processes = await getProcesses();
console.log('🚀 ~ Services ~ showSelectedProcess ~ processes:', processes);
const process = processes.find((process: any) => process.name === (elem as any).dataset.value);
const process = processes.find((process: any) => process[1].title === elem);
if (process) {
const processDiv = document.createElement('div');
processDiv.className = 'process';
processDiv.id = process.name;
processDiv.id = process[0];
const titleDiv = document.createElement('div');
titleDiv.className = 'process-title';
titleDiv.innerHTML = `${process.name} : ${process.description}`;
titleDiv.innerHTML = `${process[1].title} : ${process[1].description}`;
processDiv.appendChild(titleDiv);
for (const zone of process.zoneList) {
for (const zone of process[1].zones) {
const zoneElement = document.createElement('div');
zoneElement.className = 'process-element';
zoneElement.setAttribute('zone-id', zone.id.toString());
zoneElement.innerHTML = `Zone ${zone.id} : ${zone.name}`;
const zoneId = process[1].title + '-' + zone.id;
zoneElement.setAttribute('zone-id', zoneId);
zoneElement.setAttribute('process-title', process[1].title);
zoneElement.setAttribute('process-id', `${process[0]}_${zone.id}`);
zoneElement.innerHTML = `${zone.title}: ${zone.description}`;
const service = await Services.getInstance();
service.addSubscription(zoneElement, 'click', 'goToProcessPage');
// service.addSubscription(zoneElement, 'click', 'goToProcessPage');
zoneElement.addEventListener('click', select)
processDiv.appendChild(zoneElement);
}
if (cardContent) cardContent.appendChild(processDiv);
@ -397,65 +404,34 @@ async function showSelectedProcess(event: MouseEvent) {
}
}
function goToProcessPage(event: MouseEvent) {
const target = event.target as HTMLDivElement;
const zoneId = target?.getAttribute('zone-id');
const processList = document.querySelectorAll('.process-element');
if (processList) {
for (const process of processList) {
process.classList.remove('selected');
}
function select(event: MouseEvent) {
const target = event.target as HTMLElement;
const oldSelectedProcess = document.querySelector('.selected-process-zone')
oldSelectedProcess?.classList.remove('selected-process-zone')
if(target) {
target.classList.add('selected-process-zone')
}
target.classList.add('selected');
console.log('=======================> going to process page', zoneId);
const name = target.getAttribute('zone-id')
console.log("🚀 ~ select ~ name:", name)
}
function goToProcessPage() {
const target = document.querySelector('.selected-process-zone');
console.log("🚀 ~ goToProcessPage ~ event:", target)
if(target) {
const process = target?.getAttribute('process-id');
console.log('=======================> going to process page', process);
navigate('process-element/' + process)
}
}
(window as any).goToProcessPage = goToProcessPage
async function getProcesses(): Promise<any[]> {
const service = await Services.getInstance();
// const processes = service.getProcesses()
// console.log("🚀 ~ Services ~ getProcesses ~ processes:", processes)
const process = [
{
title: 'Messaging',
description: 'Messagerie chiffrée',
html: '<div><input /></div>',
css: '',
script: '',
zones: ['zone 1', 'zone 2'],
roles: {
owner: {
members: ['dfdsfdfdsf', 'dfdfdfdsfsfdfdsf'],
validation_rules: [
{
quorum: 1.0,
fields: ['description', 'roles', 'session_privkey', 'session_pubkey', 'key_parity'],
min_sig_member: 1.0,
},
],
},
},
},
{
title: 'Database',
description: 'Database chiffrée',
html: '<div><input /></div>',
css: '',
script: '',
zones: ['zone 1', 'zone 2'],
roles: {
owner: {
members: ['dfdsfdfdsf', 'dfdfdfdsfsfdfdsf'],
validation_rules: [
{
quorum: 1.0,
fields: ['description', 'roles', 'session_privkey', 'session_pubkey', 'key_parity'],
min_sig_member: 1.0,
},
],
},
},
},
];
return process
const processes = await service.getProcesses()
console.log("🚀 ~ Services ~ getProcesses ~ processes:", processes)
return processes
}

View File

@ -1,22 +1,32 @@
import '../public/style/4nk.css';
import { initHomePage } from './pages/home/home';
import Services from './services/service';
const routes: { [key: string]: string } = {
home: '/src/pages/home/home.html',
process: '/src/pages/process/process.html',
'process-element': '/src/pages/process-element/process-element.html',
};
export let currentRoute = ''
export async function navigate(path: string) {
path = path.replace(/^\//, '');
if (!routes[path]) {
path = 'home';
if(path.includes('/')) {
const parsedPath = path.split('/')[0]
if (!routes[parsedPath]) {
path = 'home';
}
}
await handleLocation(path);
}
async function handleLocation(path: string) {
console.log("🚀 ~ handleLocation ~ path:", path)
const parsedPath = path.split('/')
if(path.includes('/')) {
path = parsedPath[0]
}
const routeHtml = routes[path] || routes['home'];
const content = document.getElementById('containerId');
@ -30,8 +40,16 @@ async function handleLocation(path: string) {
const { initHomePage } = await import('./pages/home/home');
initHomePage();
} else if (path === 'process') {
await import('./pages/process/process');
const { init } = await import('./pages/process/process');
init()
} else if(path.includes('process-element')) {
const { initProcessElement } = await import('./pages/process-element/process-element');
if(parsedPath && parsedPath.length) {
const parseProcess = parsedPath[1].split('_')
initProcessElement(parseProcess[0], parseProcess[1])
}
}
currentRoute = path
}
}
@ -53,20 +71,20 @@ async function init(): Promise<boolean> {
await services.restoreProcesses();
await services.restoreMessages();
if (services.isPaired()) {
isPaired = true;
console.log('🚀 ~ setTimeout ~ isPaired:', isPaired);
await navigate('process');
return isPaired;
} else {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const pairingAddress = urlParams.get('sp_address');
if (pairingAddress) {
setTimeout(async () => await services.sendPairingTx(pairingAddress), 2000);
}
return isPaired;
}
// if (services.isPaired()) {
// isPaired = true;
// console.log('🚀 ~ setTimeout ~ isPaired:', isPaired);
// await navigate('process');
// return isPaired;
// } else {
// const queryString = window.location.search;
// const urlParams = new URLSearchParams(queryString);
// const pairingAddress = urlParams.get('sp_address');
// if (pairingAddress) {
// setTimeout(async () => await services.sendPairingTx(pairingAddress), 2000);
// }
// return isPaired;
// }
}, 200);
} catch (error) {
console.error(error);
@ -78,5 +96,5 @@ async function init(): Promise<boolean> {
(async () => {
const isPaired = await init();
console.log('🚀 ~ handleLocation ~ isPaired:', isPaired);
await navigate('home');
await navigate('process');
})();

View File

@ -1,15 +1,12 @@
// import { WebSocketClient } from '../websockets';
import { INotification } from '~/models/notification.model';
import homePage from '../pages/home/home.html?raw';
import homeScript from '../pages/home/home.ts?raw';
import processPage from '../pages/process/process.html?raw';
import processScript from '../pages/process/process.js?raw';
import { IProcess } from '~/models/process.model';
// import Database from './database';
import { WebSocketClient } from '../websockets';
import QRCode from 'qrcode';
import { ApiReturn } from '../../dist/pkg/sdk_client';
import Routing from './routing.service';
import { currentRoute } from '../router';
type ProcessesCache = {
[key: string]: any;
@ -511,7 +508,7 @@ export default class Services {
addSubscription(element: Element, event: string, eventHandler: string): void {
this.subscriptions.push({ element, event, eventHandler });
element.addEventListener(event, (this as any)[eventHandler].bind(this));
element.addEventListener(event, (this as any)[eventHandler]);
}
async createNewDevice() {
@ -525,10 +522,12 @@ export default class Services {
console.error('Services ~ Error:', e);
}
this.generateQRCode(spAddress || '');
//Adress to Emoji integration
this.displayEmojis(spAddress);
//Adress to Emoji integration
if(currentRoute === 'home') {
this.generateQRCode(spAddress || '');
//Adress to Emoji integration
this.displayEmojis(spAddress);
//Adress to Emoji integration
}
return spAddress;
}
@ -536,15 +535,82 @@ export default class Services {
try {
await this.sdkClient.restore_device(device);
const spAddress = this.sdkClient.get_address();
this.generateQRCode(spAddress || '');
//Adress to Emoji integration
this.displayEmojis(spAddress);
//Adress to Emoji integration
if(currentRoute === 'home') {
this.generateQRCode(spAddress || '');
//Adress to Emoji integration
this.displayEmojis(spAddress);
//Adress to Emoji integration
}
} catch (e) {
console.error(e);
}
}
async getProcesses(): Promise<any[]> {
const process = [
['1',{
title: 'Messaging',
description: 'Messagerie chiffrée',
html: '<div><input /></div>',
css: '',
script: '',
zones: [{
id: '1',
title: 'zone 1',
description: 'zone 1'
},
{
id: '2',
title: 'zone 2',
description: 'zone 2'
}],
roles: {
owner: {
members: ['dfdsfdfdsf', 'dfdfdfdsfsfdfdsf'],
validation_rules: [
{
quorum: 1.0,
fields: ['description', 'roles', 'session_privkey', 'session_pubkey', 'key_parity'],
min_sig_member: 1.0,
},
],
},
},
}],
['2',{
title: 'Database',
description: 'Database chiffrée',
html: '<div><input value="{{inputValue}}" /></div>',
css: '',
script: '',
zones: [{
id: '1',
title: 'zone 1',
description: 'zone 1'
},
{
id: '2',
title: 'zone 2',
description: 'zone 2'
}],
roles: {
owner: {
members: ['dfdsfdfdsf', 'dfdfdfdsfsfdfdsf'],
validation_rules: [
{
quorum: 1.0,
fields: ['description', 'roles', 'session_privkey', 'session_pubkey', 'key_parity'],
min_sig_member: 1.0,
},
],
},
},
}],
];
return process
}
private getProcessesCache(): ProcessesCache {
// Regular expression to match 64-character hexadecimal strings
const hexU32KeyRegex: RegExp = /^[0-9a-fA-F]{64}:\d+$/;
@ -643,6 +709,7 @@ export default class Services {
}
public async setProcessesInSelectElement(processList: any[]) {
console.log("🚀 ~ Services ~ setProcessesInSelectElement ~ processList:", processList)
const select = document.querySelector('.select-field');
if (select) {
for (const process of processList) {