+ `;
+ }
+ }*/
+ function renderList(data) {
+ // check if the finallist have any length
+ if (data.length !== 0) {
+ // get the template from the DOM
+ // get the deep copy the template
+ let node = _getTemplate.content.cloneNode(true);
+ for (let i of data) {
+ let amount = i.transactions[0].transaction.floData.match(/([0-9]+)/);
+ //
+ let cardNode = _cardTemplate.content.cloneNode(true);
+ cardNode.querySelector(".link").href = `#${i.floId}`;
+ cardNode.querySelector(".heading-name").textContent =
+ i.name;
+ cardNode.querySelector(".heading-floId").textContent =
+ i.floId;
+ cardNode.querySelector(".total-money-earned").textContent = `Total amount paid: ₹${i.totalMoneyEarned}`;
+ cardNode.querySelector(".number-of-transaction").textContent = `Total number of transactions: ${i.transactions.length}`;
+ cardNode.querySelector(".last-tx-content").textContent = getDate(i.transactions[0].transaction.time);
+ cardNode.querySelector(".last-tx-amount").textContent = `₹${amount[0]}/-`;
+ cardNode.querySelector(".intern-rating").textContent =
+ internRating[i.floId];
+ cardNode.querySelector(".blockchain-link").href =
+ i.transactions[0].transaction.blockChainLink;
+ node.querySelector(".card-wrapper").appendChild(cardNode);
+ }
+ let el = document.createElement("div");
+ el.appendChild(node);
+ return el.innerHTML;
+ }
+ else {
+ return `
+
Loading...
+ `;
+ }
+ }
+ // render the details page
+ function renderDetail() {
+ return `
+
+ `;
+ }
+ let routes = {
+ "#": renderList(finalList),
+ "#detail": renderDetail(),
+ };
+ /**
+ * Run when there is change of hash on the window
+ * - get the first value from the routes object
+ * - check the value is matching to the comming route value
+ * - if so, render the home page otherwise detail page
+ */
+ const handleRender = (route) => {
+ // ...
+ let val = Object.keys(routes)[0];
+ if (val === route) {
+ _rootDiv.innerHTML = renderList(finalList);
+ }
+ else {
+ _rootDiv.innerHTML = renderDetail();
+ }
+ };
+ // check the change in hash
+ window.addEventListener("hashchange", (e) => {
+ // get the current path
+ path.current = window.location.hash || "#";
+ /**
+ * @param always be the path of the window
+ */
+ handleRender(path.current);
+ });
+ // render the home page default
+ _rootDiv.innerHTML = renderList(finalList);
+ // Go the home page
+ _backBtn.addEventListener("click", () => {
+ window.location.hash = "";
+ _rootDiv.innerHTML = renderList();
+ });
+ /**
+ * Creating a list in which store all the
+ * flo addresses of the receiver
+ */
+ /**
+ * get the intern data from the RanchiMall
+ * - request the server for data
+ * - loop over the response
+ * - push the each data to the "finalList arrary"
+ * - call the fetchInternData()
+ */
+ async function getInternData() {
+ try {
+ let r = await floCloudAPI.requestObjectData("RIBC", {
+ application: "RIBC",
+ receiverID: "FMeiptdJNtYQEtzyYAVNP8fjsDJ1i4EPfE",
+ senderIDs: [
+ "F7TxecSPV8oFZE6Y2giVuP8hfsqfAD6erj",
+ "FCja6sLv58e3RMy41T5AmWyvXEWesqBCkX",
+ "FPFeL5PXzW9bGosUjQYCxTHSMHidnygvvd",
+ "FS4jMAcSimRMrhoRhk5cjuJERS2otiwq4A",
+ "FU2fkubqGD5ynbr7qhvaPe9DPqrNdGB6mw",
+ "FUkY9k9mVVxPzYA8uUGxUuLVH6CB83Nb9r",
+ ],
+ });
+ if (r) {
+ let i = floGlobals.appObjects.RIBC.internList;
+ for (let key in i) {
+ internList.push({
+ floId: key,
+ floUserName: i[key],
+ });
+ }
+ // fetch all the data and pack together
+ fetchInternData();
+ }
+ }
+ catch (e) {
+ // ERROR HANDLING
+ console.log("Error Occur while fetching the Intern Data", e);
+ _rootDiv.innerHTML = `
+
+
Something Went Wrong [keep Refreshing the page ...]
+
${e}
+
+ `;
+ }
+ }
+ function bundleAllData() {
+ // get the internList from the server
+ let internList = floGlobals.appObjects.RIBC.internList;
+ internRating = floGlobals.appObjects.RIBC.internRating;
+ // get the intern Assigned project from the server
+ // get the value of internAssigned project
+ let internsAssigned = floGlobals.appObjects.RIBC.internsAssigned;
+ const internsAssignedArr = Object.entries(internsAssigned);
+ // get the project details from the server
+ let projectDetails = floGlobals.appObjects.RIBC.projectDetails;
+ // set the tragetList
+ let tragetList = [];
+ // loop over the intern data
+ for (let internId in internList) {
+ // loop over the intern assigned project so we get the
+ // associated projects
+ for (let [k, v] of internsAssignedArr) {
+ // find the value have correct key
+ let value = v.find((el) => {
+ return el === internId;
+ });
+ // if we find a value find its project Details also
+ if (value) {
+ // loop over the project details
+ for (let i in projectDetails) {
+ // find out the key by the slicing it
+ let key = k.slice(0, 14);
+ // get the value
+ let newVal = projectDetails[key];
+ // send it ot targetList
+ tragetList.push({
+ internId: internId,
+ project: k,
+ projectName: newVal.projectName,
+ });
+ }
+ }
+ }
+ }
+ // loop over to the finalList to attach the projectName
+ for (let key in tragetList) {
+ // get the internId from the tragetList
+ let val = tragetList[key].internId;
+ // get the index out of that
+ let index = finalList.findIndex((el) => el.floId === val);
+ // if it exists
+ if (index > -1) {
+ finalList[index].projectName = tragetList[key].projectName;
+ }
+ }
+ for (let index of finalList) {
+ let totalAmount = 0;
+ /*finalList[index].transactions.forEach((intern) => {
+ let amount = intern.transaction.floData.match(/([0-9]+)/);
+ let num = Number(amount[0]);
+ totalAmount += num;
+ });
+ finalList[index].totalMoneyEarned = totalAmount;*/
+ }
+ /**
+ * Loop over the final List to get the totalAmount
+ */
+ finalList.forEach((list) => {
+ let totalAmount = 0;
+ list.transactions.forEach((intern) => {
+ let amount = intern.transaction.floData.match(/([0-9]+)/);
+ let num = Number(amount[0]);
+ totalAmount += num;
+ // add the blockChainLink key
+ //intern.transaction.blockChainLink = `https://livenet.flocha.in/block/${intern.transaction.blockhash}`;
+ intern.transaction.blockChainLink = `https://livenet.flocha.in/tx/${intern.transaction.txid}`;
+ });
+ const transactionsDetails = list.transactions.sort((first, second) => {
+ return second.transaction.time - first.transaction.time;
+ });
+ list.totalMoneyEarned = totalAmount;
+ list.transactions = transactionsDetails;
+ });
+ const myArr = finalList.sort((first, second) => {
+ return (second.transactions[0].transaction.time -
+ first.transactions[0].transaction.time);
+ });
+ finalList = myArr;
+ }
+ // get the internData after the 3sec I don't know why
+ setTimeout(() => {
+ getInternData();
+ }, 1000);
+ /**
+ * Fetch initial transactions
+ * - request the distributer transactions from the server
+ * - push all these transactions to the "receiverList array"
+ * - then loop over to the interList array in which we collect interns data
+ * - filter out the transactions of the intern from the distributer transactions
+ * - push all the data to the new called finalList array
+ * - render the home page to the DOM
+ */
+ function fetchInternData() {
+ floBlockchainAPI
+ .readAllTxs("FThgnJLcuStugLc24FJQggmp2WgaZjrBSn", "", "")
+ .then((r) => {
+ // loop over the response transactions
+ r.forEach((user) => {
+ // sending all the transaction to the new array
+ receiverList.push({
+ floId: user.vout[0].scriptPubKey.addresses[0],
+ transaction: user,
+ });
+ });
+ // loop over the intern data
+ for (let d of internList) {
+ // filter the intern transactions
+ const result = receiverList.filter((i) => {
+ return i.floId === d.floId;
+ });
+ // check if the transaction are available
+ if (result.length) {
+ // add all the transaction to the new Array
+ finalList.push({
+ name: d.floUserName,
+ floId: d.floId,
+ transactions: [...result],
+ });
+ }
+ }
+ bundleAllData();
+ // re-render the DOM
+ _rootDiv.innerHTML = renderList(finalList);
+ }, console.error);
+ }
+ }
+};
diff --git a/js/main_UI.js b/js/main_UI.js
new file mode 100644
index 0000000..9057041
--- /dev/null
+++ b/js/main_UI.js
@@ -0,0 +1,305 @@
+// Global variables
+const appPages = ['dashboard', 'settings'];
+const domRefs = {};
+let timerId;
+const currentYear = new Date().getFullYear();
+
+//Checks for internet connection status
+if (!navigator.onLine)
+ notify(
+ "There seems to be a problem connecting to the internet, Please check you internet connection.",
+ "error",
+ { sound: true }
+ );
+window.addEventListener("offline", () => {
+ notify(
+ "There seems to be a problem connecting to the internet, Please check you internet connection.",
+ "error",
+ { pinned: true, sound: true }
+ );
+});
+window.addEventListener("online", () => {
+ getRef("notification_drawer").clearAll();
+ notify("We are back online.", "success");
+});
+
+// Use instead of document.getElementById
+function getRef(elementId) {
+ if (!domRefs.hasOwnProperty(elementId)) {
+ domRefs[elementId] = {
+ count: 1,
+ ref: null,
+ };
+ return document.getElementById(elementId);
+ } else {
+ if (domRefs[elementId].count < 3) {
+ domRefs[elementId].count = domRefs[elementId].count + 1;
+ return document.getElementById(elementId);
+ } else {
+ if (!domRefs[elementId].ref)
+ domRefs[elementId].ref = document.getElementById(elementId);
+ return domRefs[elementId].ref;
+ }
+ }
+}
+
+// returns dom with specified element
+function createElement(tagName, options) {
+ const { className, textContent, innerHTML, attributes = {} } = options
+ const elem = document.createElement(tagName)
+ for (let attribute in attributes) {
+ elem.setAttribute(attribute, attributes[attribute])
+ }
+ if (className)
+ elem.className = className
+ if (textContent)
+ elem.textContent = textContent
+ if (innerHTML)
+ elem.innerHTML = innerHTML
+ return elem
+}
+
+// Use when a function needs to be executed after user finishes changes
+const debounce = (callback, wait) => {
+ let timeoutId = null;
+ return (...args) => {
+ window.clearTimeout(timeoutId);
+ timeoutId = window.setTimeout(() => {
+ callback.apply(null, args);
+ }, wait);
+ };
+}
+
+// Limits the rate of function execution
+function throttle(func, delay) {
+ // If setTimeout is already scheduled, no need to do anything
+ if (timerId) {
+ return;
+ }
+
+ // Schedule a setTimeout after delay seconds
+ timerId = setTimeout(function () {
+ func();
+
+ // Once setTimeout function execution is finished, timerId = undefined so that in
+ // the next scroll event function execution can be scheduled by the setTimeout
+ timerId = undefined;
+ }, delay);
+}
+
+class Stack {
+ constructor() {
+ this.items = [];
+ }
+ push(element) {
+ this.items.push(element);
+ }
+ pop() {
+ if (this.items.length == 0)
+ return "Underflow";
+ return this.items.pop();
+ }
+ peek() {
+ return this.items[this.items.length - 1];
+ }
+}
+
+// function required for popups or modals to appear
+function showPopup(popupId, pinned) {
+ zIndex++
+ getRef(popupId).setAttribute('style', `z-index: ${zIndex}`)
+ popupStack = getRef(popupId).show({ pinned, popupStack })
+ return getRef(popupId);
+}
+
+// hides the popup or modal
+function hidePopup() {
+ if (popupStack.peek() === undefined)
+ return;
+ popupStack.peek().popup.hide()
+}
+
+// displays a popup for asking permission. Use this instead of JS confirm
+const getConfirmation = (title, message, cancelText = 'Cancel', confirmText = 'OK') => {
+ return new Promise(resolve => {
+ showPopup('confirmation_popup', true)
+ getRef('confirm_title').textContent = title;
+ getRef('confirm_message').textContent = message;
+ let cancelButton = getRef('confirmation_popup').children[2].children[0],
+ submitButton = getRef('confirmation_popup').children[2].children[1]
+ submitButton.textContent = confirmText
+ cancelButton.textContent = cancelText
+ submitButton.onclick = () => {
+ hidePopup()
+ resolve(true);
+ }
+ cancelButton.onclick = () => {
+ hidePopup()
+ resolve(false);
+ }
+ })
+}
+
+// displays a popup for asking user input. Use this instead of JS prompt
+async function getPromptInput(title, message = '', isPassword = true, cancelText = 'Cancel', confirmText = 'OK') {
+ showPopup('prompt_popup', true)
+ getRef('prompt_title').textContent = title;
+ let input = getRef('prompt_input');
+ input.setAttribute("placeholder", message)
+ let buttons = getRef('prompt_popup').querySelectorAll("sm-button");
+ if (isPassword)
+ input.setAttribute("type", "text")
+ else
+ input.setAttribute("type", "password")
+ input.focusIn()
+ buttons[0].textContent = cancelText;
+ buttons[1].textContent = confirmText;
+ return new Promise((resolve, reject) => {
+ buttons[0].onclick = () => {
+ hidePopup()
+ return;
+ }
+ buttons[1].onclick = () => {
+ let value = input.value;
+ hidePopup()
+ resolve(value)
+ }
+ })
+}
+
+//Function for displaying toast notifications. pass in error for mode param if you want to show an error.
+function notify(message, mode, options = {}) {
+ const { pinned = false, sound = false } = options
+ let icon
+ switch (mode) {
+ case 'success':
+ icon = ``
+ break;
+ case 'error':
+ icon = ``
+ break;
+ }
+ getRef("notification_drawer").push(message, { pinned, icon });
+ if (navigator.onLine && sound) {
+ getRef("notification_sound").currentTime = 0;
+ getRef("notification_sound").play();
+ }
+}
+
+function getFormatedTime(time, relative) {
+ try {
+ if (String(time).indexOf("_")) time = String(time).split("_")[0];
+ const intTime = parseInt(time);
+ if (String(intTime).length < 13) time *= 1000;
+ let timeFrag = new Date(intTime).toString().split(" "),
+ day = timeFrag[0],
+ month = timeFrag[1],
+ date = timeFrag[2],
+ year = timeFrag[3],
+ minutes = new Date(intTime).getMinutes(),
+ hours = new Date(intTime).getHours(),
+ currentTime = new Date().toString().split(" ");
+
+ minutes = minutes < 10 ? `0${minutes}` : minutes;
+ let finalHours = ``;
+ if (hours > 12) finalHours = `${hours - 12}:${minutes}`;
+ else if (hours === 0) finalHours = `12:${minutes}`;
+ else finalHours = `${hours}:${minutes}`;
+
+ finalHours = hours >= 12 ? `${finalHours} PM` : `${finalHours} AM`;
+ if (relative) {
+ return `${date} ${month} ${year}`;
+ } else return `${finalHours} ${month} ${date} ${year}`;
+ } catch (e) {
+ console.error(e);
+ return time;
+ }
+}
+
+window.addEventListener('hashchange', e => showPage(window.location.hash))
+window.addEventListener("load", () => {
+ document.body.classList.remove('hide-completely')
+ showPage(window.location.hash)
+ // document.querySelectorAll('sm-input[data-flo-id]').forEach(input => input.customValidation = validateAddr)
+ document.addEventListener('keyup', (e) => {
+ if (e.code === 'Escape') {
+ hidePopup()
+ }
+ })
+ document.addEventListener("pointerdown", (e) => {
+ if (e.target.closest("button, sm-button:not([disabled]), .interact")) {
+ createRipple(e, e.target.closest("button, sm-button, .interact"));
+ }
+ });
+ document.addEventListener('copy', () => {
+ notify('copied', 'success')
+ })
+});
+
+function createRipple(event, target) {
+ const circle = document.createElement("span");
+ const diameter = Math.max(target.clientWidth, target.clientHeight);
+ const radius = diameter / 2;
+ const targetDimensions = target.getBoundingClientRect();
+ circle.style.width = circle.style.height = `${diameter}px`;
+ circle.style.left = `${event.clientX - (targetDimensions.left + radius)}px`;
+ circle.style.top = `${event.clientY - (targetDimensions.top + radius)}px`;
+ circle.classList.add("ripple");
+ const rippleAnimation = circle.animate(
+ [
+ {
+ transform: "scale(3)",
+ opacity: 0,
+ },
+ ],
+ {
+ duration: 1000,
+ fill: "forwards",
+ easing: "ease-out",
+ }
+ );
+ target.append(circle);
+ rippleAnimation.onfinish = () => {
+ circle.remove();
+ };
+}
+
+function showPage(targetPage, options = {}) {
+ const { firstLoad, hashChange } = options
+ let pageId
+ if (targetPage === '') {
+ pageId = 'overview_page'
+ }
+ else {
+ pageId = targetPage.includes('#') ? targetPage.split('#')[1] : targetPage
+ }
+ if(!appPages.includes(pageId)) return
+ document.querySelector('.page:not(.hide-completely)').classList.add('hide-completely')
+ document.querySelector('.nav-list__item--active').classList.remove('nav-list__item--active')
+ getRef(pageId).classList.remove('hide-completely')
+ getRef(pageId).animate([
+ {
+ opacity: 0,
+ transform: 'translateX(-1rem)'
+ },
+ {
+ opacity: 1,
+ transform: 'none'
+ },
+ ],
+ {
+ duration: 300,
+ easing: 'ease'
+ })
+ const targetListItem = document.querySelector(`.nav-list__item[href="#${pageId}"]`)
+ targetListItem.classList.add('nav-list__item--active')
+ if (firstLoad && window.innerWidth > 640 && targetListItem.getBoundingClientRect().top > getRef('side_nav').getBoundingClientRect().height) {
+ getRef('side_nav').scrollTo({
+ top: (targetListItem.getBoundingClientRect().top - getRef('side_nav').getBoundingClientRect().top + getRef('side_nav').scrollTop),
+ behavior: 'smooth'
+ })
+ }
+ if (hashChange && window.innerWidth < 640) {
+ getRef('side_nav').close()
+ }
+}
\ No newline at end of file