From d7da806989e173638543a657e73ac9b432d9a0aa Mon Sep 17 00:00:00 2001 From: AnisHADJARAB Date: Wed, 30 Oct 2024 10:34:02 +0000 Subject: [PATCH] fix process list page and add process page --- public/style/4nk.css | 58 ++++++++ .../process-element/process-element.html | 35 +++++ src/pages/process-element/process-element.ts | 55 ++++++++ src/pages/process/process.html | 10 +- src/pages/process/process.ts | 132 +++++++----------- src/router.ts | 56 +++++--- src/services/service.ts | 93 ++++++++++-- 7 files changed, 324 insertions(+), 115 deletions(-) create mode 100644 src/pages/process-element/process-element.html create mode 100644 src/pages/process-element/process-element.ts diff --git a/public/style/4nk.css b/public/style/4nk.css index e40db44..bbe2b2c 100644 --- a/public/style/4nk.css +++ b/public/style/4nk.css @@ -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%; } \ No newline at end of file diff --git a/src/pages/process-element/process-element.html b/src/pages/process-element/process-element.html new file mode 100644 index 0000000..f2f466e --- /dev/null +++ b/src/pages/process-element/process-element.html @@ -0,0 +1,35 @@ + + + +
+

Process {{processTitle}}

+
+ +
+
+ diff --git a/src/pages/process-element/process-element.ts b/src/pages/process-element/process-element.ts new file mode 100644 index 0000000..f891695 --- /dev/null +++ b/src/pages/process-element/process-element.ts @@ -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 { + const service = await Services.getInstance(); + const processes = await service.getProcesses() + console.log("πŸš€ ~ Services ~ getProcesses ~ processes:", processes) + + return processes +} diff --git a/src/pages/process/process.html b/src/pages/process/process.html index fa8bc89..c795975 100644 --- a/src/pages/process/process.html +++ b/src/pages/process/process.html @@ -31,19 +31,19 @@
-
-
+
+
-
+
-
- OK +
+ OK
diff --git a/src/pages/process/process.ts b/src/pages/process/process.ts index 58c524d..2cb80ed 100644 --- a/src/pages/process/process.ts +++ b/src/pages/process/process.ts @@ -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; -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 { const service = await Services.getInstance(); - // const processes = service.getProcesses() - // console.log("πŸš€ ~ Services ~ getProcesses ~ processes:", processes) - const process = [ - { - title: 'Messaging', - description: 'Messagerie chiffrΓ©e', - html: '
', - 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: '
', - 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 } diff --git a/src/router.ts b/src/router.ts index 8f48b4c..ef90ed9 100644 --- a/src/router.ts +++ b/src/router.ts @@ -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 { 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 { (async () => { const isPaired = await init(); console.log('πŸš€ ~ handleLocation ~ isPaired:', isPaired); - await navigate('home'); + await navigate('process'); })(); diff --git a/src/services/service.ts b/src/services/service.ts index 7635bfb..d8eb583 100644 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -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 { + const process = [ + ['1',{ + title: 'Messaging', + description: 'Messagerie chiffrΓ©e', + html: '
', + 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: '
', + 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) {