ribc/index.html
2022-10-04 17:52:29 +05:30

2957 lines
173 KiB
HTML

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>RanchiMall Internships</title>
<meta name="description" content="Web app for managing interns and projects">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/main.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Calistoga&family=Roboto:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap"
rel="stylesheet">
<script src="components.js" defer></script>
<script id="floGlobals">
/* Constants for FLO blockchain operations !!Make sure to add this at begining!! */
const floGlobals = {
blockchain: "FLO",
adminID: "FKAEdnPfjXLHSYwrXQu377ugN4tXU7VGdf", // "FMeiptdJNtYQEtzyYAVNP8fjsDJ1i4EPfE",
application: "TEST_MODE" // "InternManagement"
}
</script>
<script src="scripts/lib.js"></script>
<script src="scripts/floCrypto.js"></script>
<script src="scripts/floBlockchainAPI.js"></script>
<script src="scripts/compactIDB.js"></script>
<script src="scripts/floCloudAPI.js"></script>
<script src="scripts/floDapps.js"></script>
<script src="https://unpkg.com/uhtml@3.0.1/es.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fuse.js@6.4.6" defer></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@2.3.0/dist/purify.min.js" defer></script>
<script src="scripts/ribc.js"></script>
<script id="onLoadStartUp">
function onLoadStartUp() {
routeTo('loading')
document.body.classList.remove('hidden')
floDapps.setCustomPrivKeyInput(getSignedIn);
floDapps.launchStartUp().then(result => {
console.log(result)
console.log(`Welcome FLO_ID: ${myFloID}`)
RIBC.init(floGlobals.subAdmins.includes(myFloID)).then(result => {
console.log(result)
renderAllElements()
routeTo(window.location.hash, { firstLoad: true })
}).catch(error => console.error(error))
}).catch(error => console.error(error))
}
</script>
</head>
<body onload="onLoadStartUp()" class="hidden">
<sm-notifications id="notification_drawer"></sm-notifications>
<sm-popup id="confirmation_popup">
<h4 id="confirm_title"></h4>
<p id="confirm_message"></p>
<div class="flex align-center">
<sm-button variant="no-outline" class="cancel-button">Cancel</sm-button>
<sm-button variant="no-outline" class="confirm-button">OK</button>
</div>
</sm-popup>
<div id="secondary_pages" class="page">
<header class="flex align-center gap-1">
<div class="flex align-center flex-1">
<svg class="icon margin-right-0-3" viewBox="0 0 27.25 32">
<title>RanchiMall</title>
<path
d="M27.14,30.86c-.74-2.48-3-4.36-8.25-6.94a20,20,0,0,1-4.2-2.49,6,6,0,0,1-1.25-1.67,4,4,0,0,1,0-2.26c.37-1.08.79-1.57,3.89-4.55a11.66,11.66,0,0,0,3.34-4.67,6.54,6.54,0,0,0,.05-2.82C20,3.6,18.58,2,16.16.49c-.89-.56-1.29-.64-1.3-.24a3,3,0,0,1-.3.72l-.3.55L13.42.94C13,.62,12.4.26,12.19.15c-.4-.2-.73-.18-.72.05a9.39,9.39,0,0,1-.61,1.33s-.14,0-.27-.13C8.76.09,8-.27,8,.23A11.73,11.73,0,0,1,6.76,2.6C4.81,5.87,2.83,7.49.77,7.49c-.89,0-.88,0-.61,1,.22.85.33.92,1.09.69A5.29,5.29,0,0,0,3,8.33c.23-.17.45-.29.49-.26a2,2,0,0,1,.22.63A1.31,1.31,0,0,0,4,9.34a5.62,5.62,0,0,0,2.27-.87L7,8l.13.55c.19.74.32.82,1,.65a7.06,7.06,0,0,0,3.46-2.47l.6-.71-.06.64c-.17,1.63-1.3,3.42-3.39,5.42L6.73,14c-3.21,3.06-3,5.59.6,8a46.77,46.77,0,0,0,4.6,2.41c.28.13,1,.52,1.59.87,3.31,2,4.95,3.92,4.95,5.93a2.49,2.49,0,0,0,.07.77h0c.09.09,0,.1.9-.14a2.61,2.61,0,0,0,.83-.32,3.69,3.69,0,0,0-.55-1.83A11.14,11.14,0,0,0,17,26.81a35.7,35.7,0,0,0-5.1-2.91C9.37,22.64,8.38,22,7.52,21.17a3.53,3.53,0,0,1-1.18-2.48c0-1.38.71-2.58,2.5-4.23,2.84-2.6,3.92-3.91,4.67-5.65a3.64,3.64,0,0,0,.42-2A3.37,3.37,0,0,0,13.61,5l-.32-.74.29-.48c.17-.27.37-.63.46-.8l.15-.3.44.64a5.92,5.92,0,0,1,1,2.81,5.86,5.86,0,0,1-.42,1.94c0,.12-.12.3-.15.4a9.49,9.49,0,0,1-.67,1.1,28,28,0,0,1-4,4.29C8.62,15.49,8.05,16.44,8,17.78a3.28,3.28,0,0,0,1.11,2.76c.95,1,2.07,1.74,5.25,3.32,3.64,1.82,5.22,2.9,6.41,4.38A4.78,4.78,0,0,1,21.94,31a3.21,3.21,0,0,0,.14.92,1.06,1.06,0,0,0,.43-.05l.83-.22.46-.12-.06-.46c-.21-1.53-1.62-3.25-3.94-4.8a37.57,37.57,0,0,0-5.22-2.82A13.36,13.36,0,0,1,11,21.19a3.36,3.36,0,0,1-.8-4.19c.41-.85.83-1.31,3.77-4.15,2.39-2.31,3.43-4.13,3.43-6a5.85,5.85,0,0,0-2.08-4.29c-.23-.21-.44-.43-.65-.65A2.5,2.5,0,0,1,15.27.69a10.6,10.6,0,0,1,2.91,2.78A4.16,4.16,0,0,1,19,6.16a4.91,4.91,0,0,1-.87,3c-.71,1.22-1.26,1.82-4.27,4.67a9.47,9.47,0,0,0-2.07,2.6,2.76,2.76,0,0,0-.33,1.54,2.76,2.76,0,0,0,.29,1.47c.57,1.21,2.23,2.55,4.65,3.73a32.41,32.41,0,0,1,5.82,3.24c2.16,1.6,3.2,3.16,3.2,4.8a1.94,1.94,0,0,0,.09.76,4.54,4.54,0,0,0,1.66-.4C27.29,31.42,27.29,31.37,27.14,30.86ZM6.1,7h0a3.77,3.77,0,0,1-1.46.45L4,7.51l.68-.83a25.09,25.09,0,0,0,3-4.82A12,12,0,0,1,8.28.76c.11-.12.77.32,1.53,1l.63.58-.57.84A10.34,10.34,0,0,1,6.1,7Zm5.71-1.78A9.77,9.77,0,0,1,9.24,7.18h0a5.25,5.25,0,0,1-1.17.28l-.58,0,.65-.78a21.29,21.29,0,0,0,2.1-3.12c.22-.41.42-.76.44-.79s.5.43.9,1.24L12,5ZM13.41,3a2.84,2.84,0,0,1-.45.64,11,11,0,0,1-.9-.91l-.84-.9.19-.45c.34-.79.39-.8,1-.31A9.4,9.4,0,0,1,13.8,2.33q-.18.34-.39.69Z">
</path>
</svg>
<h4>RanchiMall Internships</h4>
</div>
<theme-toggle></theme-toggle>
</header>
<div id="landing" class="grid inner-page hidden">
<div class="flex flex-wrap space-between gap-1-5 landing__card">
<div class="grid gap-1-5">
<h1 class="calistoga" style="max-width: 26rem;">
Blockchain based Internship Platform
</h1>
<div class="flex gap-0-3">
<a href="#/sign_up" class="button" style="background-color: rgba( 0 0 0 / 0.3);">Get
started</a>
<a href="#/sign_in" class="button button--primary">Sign In</a>
</div>
</div>
<img src="assets/working-intern.svg" alt="">
</div>
</div>
<article id="sign_in" class="inner-page hidden">
<section>
<h1 style="font-size: 2rem;">Sign In</h1>
<p>Welcome back, glad to see you again</p>
<sm-form id="sign_in_form">
<sm-input id="private_key_field" class="password-field" type="password"
placeholder="FLO private key" error-text="Private key is invalid" data-private-key required>
<label slot="right" class="interact">
<input type="checkbox" class="hidden" autocomplete="off" readonly
onchange="togglePrivateKeyVisibility(this)">
<svg class="icon invisible" xmlns="http://www.w3.org/2000/svg" height="24px"
viewBox="0 0 24 24" width="24px" fill="#000000">
<title>Hide password</title>
<path d="M0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0z" fill="none" />
<path
d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z" />
</svg>
<svg class="icon visible" xmlns="http://www.w3.org/2000/svg" height="24px"
viewBox="0 0 24 24" width="24px" fill="#000000">
<title>Show password</title>
<path d="M0 0h24v24H0z" fill="none" />
<path
d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" />
</svg>
</label>
</sm-input>
<sm-button id="sign_in_button" variant="primary" disabled>Sign In</sm-button>
</sm-form>
<p>
New here? <a href="#/sign_up">get your FLO login credentials</a>
</p>
</section>
</article>
<article id="sign_up" class="inner-page hidden">
<section class="grid gap-1-5">
<div id="flo_id_warning" class="flex gap-1">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" />
</svg>
<div class="grid gap-0-5">
<strong>
<h3>
Keep your keys safe!
</h3>
</strong>
<p>Don't share with anyone. Once lost private key can't be
recovered.</p>
</div>
</div>
<div class="grid gap-1-5 generated-keys-wrapper">
<div class="grid gap-0-5">
<h5>FLO address</h5>
<sm-copy id="generated_flo_address"></sm-copy>
</div>
<div class="grid gap-0-5">
<h5>Private key</h5>
<sm-copy id="generated_private_key"></sm-copy>
</div>
</div>
<div class="flex gap-0-5">
<button class="button" style="flex-shrink: 0;" onclick="downloadGeneratedCredentials()">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg"
enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<path
d="M18,15v3H6v-3H4v3c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-3H18z M17,11l-1.41-1.41L13,12.17V4h-2v8.17L8.41,9.59L7,11l5,5 L17,11z" />
</g>
</svg>
Download</button>
<button id="sign_up_button" class="button button--primary w-100">Sign in with these</button>
</div>
<p class="margin-top-1">You can use these FLO credentials with other RanchiMall apps
too. </p>
</section>
</article>
<div id="loading" class="inner-page hidden">
<cube-loader></cube-loader>
<h4 class="page__tag-line margin-block-1">Getting everything ready, Hang on.</h4>
<button class="button" onclick="floDapps.clearCredentials()">Reset</button>
</div>
</div>
<main id="main_page" class="grid page hidden">
<header id="main_header" class="hide-on-desktop space-between">
<div id="logo" class="logo">
<svg class="icon rm-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M20.46,21.32C20,19.78,18.6,18.59,15.3,17a12.67,12.67,0,0,1-2.64-1.56,4.27,4.27,0,0,1-.79-1,2.6,2.6,0,0,1,0-1.41c.24-.68.49-1,2.43-2.85a7.18,7.18,0,0,0,2.09-2.92,4.25,4.25,0,0,0,0-1.77,6.52,6.52,0,0,0-2.85-3.11c-.56-.36-.81-.4-.81-.15a2.33,2.33,0,0,1-.18.45L12.4,3l-.53-.36c-.28-.21-.64-.41-.77-.49s-.46-.11-.46,0a6.21,6.21,0,0,1-.37.83s-.08,0-.17-.08c-1.15-.83-1.64-1-1.64-.73A7.33,7.33,0,0,1,7.7,3.65C6.48,5.68,5.24,6.7,4,6.7c-.56,0-.54,0-.37.64s.2.58.68.43a3.37,3.37,0,0,0,1.09-.54.86.86,0,0,1,.3-.17,1.34,1.34,0,0,1,.13.39.79.79,0,0,0,.17.4A3.5,3.5,0,0,0,7.37,7.3L7.8,7l.09.34c.12.45.19.51.62.39a4.25,4.25,0,0,0,2.17-1.54l.38-.45,0,.39A5.92,5.92,0,0,1,8.89,9.54L7.67,10.71c-2,1.93-1.89,3.51.37,5a27.41,27.41,0,0,0,2.89,1.51c.17.07.62.32,1,.54C14,19,15,20.23,15,21.48a2,2,0,0,0,0,.49h0c0,.05,0,.05.56-.1a1.89,1.89,0,0,0,.53-.21,2.41,2.41,0,0,0-.34-1.15,7.05,7.05,0,0,0-1.68-1.77,21.91,21.91,0,0,0-3.2-1.83A9.53,9.53,0,0,1,8.16,15.2a2.18,2.18,0,0,1-.74-1.55C7.42,12.79,7.86,12,9,11c1.77-1.64,2.45-2.45,2.92-3.55a2.28,2.28,0,0,0,.26-1.26A2,2,0,0,0,12,5.06l-.2-.45L12,4.3l.28-.49.09-.18L12.6,4a3.69,3.69,0,0,1,.61,1.76A3.47,3.47,0,0,1,12.94,7l-.09.25s-.21.37-.41.69A17.78,17.78,0,0,1,9.91,10.6c-1.07,1-1.43,1.62-1.47,2.47a2.05,2.05,0,0,0,.7,1.73,10.47,10.47,0,0,0,3.28,2.08c2.28,1.13,3.26,1.81,4,2.73a2.94,2.94,0,0,1,.74,1.75,1.26,1.26,0,0,0,.09.57.48.48,0,0,0,.26,0l.51-.13.29-.08,0-.28c-.13-1-1-2-2.47-3a25.52,25.52,0,0,0-3.26-1.77,8.59,8.59,0,0,1-2.23-1.43,2.09,2.09,0,0,1-.5-2.62c.26-.53.5-.83,2.35-2.6,1.51-1.45,2.15-2.58,2.15-3.79A3.67,3.67,0,0,0,13,3.48a3,3,0,0,1-.4-.42A1.85,1.85,0,0,1,13,2.33a6.74,6.74,0,0,1,1.83,1.73,2.62,2.62,0,0,1,.47,1.68,3,3,0,0,1-.55,1.84c-.45.78-.79,1.14-2.67,2.93a5.56,5.56,0,0,0-1.3,1.64,1.77,1.77,0,0,0-.21,1,1.76,1.76,0,0,0,.19.92,6.28,6.28,0,0,0,2.9,2.34,21.6,21.6,0,0,1,3.66,2c1.35,1,2,2,2,3a1.06,1.06,0,0,0,.05.47,2.83,2.83,0,0,0,1-.24C20.56,21.68,20.56,21.66,20.46,21.32ZM7.29,6.4h0a2.23,2.23,0,0,1-.9.28L6,6.72l.43-.53a15.22,15.22,0,0,0,1.89-3,3.52,3.52,0,0,1,.38-.67c.07-.08.49.2,1,.64l.39.35L9.66,4A6.7,6.7,0,0,1,7.29,6.4Zm3.58-1.11A5.8,5.8,0,0,1,9.25,6.51h0a3.3,3.3,0,0,1-.74.17l-.35,0,.39-.49a15.64,15.64,0,0,0,1.32-2,4.63,4.63,0,0,1,.28-.49c.06-.08.33.26.57.77l.28.57Zm1-1.4a1.63,1.63,0,0,1-.28.4A6.63,6.63,0,0,1,11,3.72l-.53-.56.12-.29c.2-.49.24-.51.64-.19a5.57,5.57,0,0,1,.85.78A2.78,2.78,0,0,1,11.87,3.89Z" />
</svg>
<h4>RIBC</h4>
</div>
<theme-toggle></theme-toggle>
</header>
<nav id="main_nav">
<div class="nav-list__item hide-on-mobile">
<svg class="icon rm-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M20.46,21.32C20,19.78,18.6,18.59,15.3,17a12.67,12.67,0,0,1-2.64-1.56,4.27,4.27,0,0,1-.79-1,2.6,2.6,0,0,1,0-1.41c.24-.68.49-1,2.43-2.85a7.18,7.18,0,0,0,2.09-2.92,4.25,4.25,0,0,0,0-1.77,6.52,6.52,0,0,0-2.85-3.11c-.56-.36-.81-.4-.81-.15a2.33,2.33,0,0,1-.18.45L12.4,3l-.53-.36c-.28-.21-.64-.41-.77-.49s-.46-.11-.46,0a6.21,6.21,0,0,1-.37.83s-.08,0-.17-.08c-1.15-.83-1.64-1-1.64-.73A7.33,7.33,0,0,1,7.7,3.65C6.48,5.68,5.24,6.7,4,6.7c-.56,0-.54,0-.37.64s.2.58.68.43a3.37,3.37,0,0,0,1.09-.54.86.86,0,0,1,.3-.17,1.34,1.34,0,0,1,.13.39.79.79,0,0,0,.17.4A3.5,3.5,0,0,0,7.37,7.3L7.8,7l.09.34c.12.45.19.51.62.39a4.25,4.25,0,0,0,2.17-1.54l.38-.45,0,.39A5.92,5.92,0,0,1,8.89,9.54L7.67,10.71c-2,1.93-1.89,3.51.37,5a27.41,27.41,0,0,0,2.89,1.51c.17.07.62.32,1,.54C14,19,15,20.23,15,21.48a2,2,0,0,0,0,.49h0c0,.05,0,.05.56-.1a1.89,1.89,0,0,0,.53-.21,2.41,2.41,0,0,0-.34-1.15,7.05,7.05,0,0,0-1.68-1.77,21.91,21.91,0,0,0-3.2-1.83A9.53,9.53,0,0,1,8.16,15.2a2.18,2.18,0,0,1-.74-1.55C7.42,12.79,7.86,12,9,11c1.77-1.64,2.45-2.45,2.92-3.55a2.28,2.28,0,0,0,.26-1.26A2,2,0,0,0,12,5.06l-.2-.45L12,4.3l.28-.49.09-.18L12.6,4a3.69,3.69,0,0,1,.61,1.76A3.47,3.47,0,0,1,12.94,7l-.09.25s-.21.37-.41.69A17.78,17.78,0,0,1,9.91,10.6c-1.07,1-1.43,1.62-1.47,2.47a2.05,2.05,0,0,0,.7,1.73,10.47,10.47,0,0,0,3.28,2.08c2.28,1.13,3.26,1.81,4,2.73a2.94,2.94,0,0,1,.74,1.75,1.26,1.26,0,0,0,.09.57.48.48,0,0,0,.26,0l.51-.13.29-.08,0-.28c-.13-1-1-2-2.47-3a25.52,25.52,0,0,0-3.26-1.77,8.59,8.59,0,0,1-2.23-1.43,2.09,2.09,0,0,1-.5-2.62c.26-.53.5-.83,2.35-2.6,1.51-1.45,2.15-2.58,2.15-3.79A3.67,3.67,0,0,0,13,3.48a3,3,0,0,1-.4-.42A1.85,1.85,0,0,1,13,2.33a6.74,6.74,0,0,1,1.83,1.73,2.62,2.62,0,0,1,.47,1.68,3,3,0,0,1-.55,1.84c-.45.78-.79,1.14-2.67,2.93a5.56,5.56,0,0,0-1.3,1.64,1.77,1.77,0,0,0-.21,1,1.76,1.76,0,0,0,.19.92,6.28,6.28,0,0,0,2.9,2.34,21.6,21.6,0,0,1,3.66,2c1.35,1,2,2,2,3a1.06,1.06,0,0,0,.05.47,2.83,2.83,0,0,0,1-.24C20.56,21.68,20.56,21.66,20.46,21.32ZM7.29,6.4h0a2.23,2.23,0,0,1-.9.28L6,6.72l.43-.53a15.22,15.22,0,0,0,1.89-3,3.52,3.52,0,0,1,.38-.67c.07-.08.49.2,1,.64l.39.35L9.66,4A6.7,6.7,0,0,1,7.29,6.4Zm3.58-1.11A5.8,5.8,0,0,1,9.25,6.51h0a3.3,3.3,0,0,1-.74.17l-.35,0,.39-.49a15.64,15.64,0,0,0,1.32-2,4.63,4.63,0,0,1,.28-.49c.06-.08.33.26.57.77l.28.57Zm1-1.4a1.63,1.63,0,0,1-.28.4A6.63,6.63,0,0,1,11,3.72l-.53-.56.12-.29c.2-.49.24-.51.64-.19a5.57,5.57,0,0,1,.85.78A2.78,2.78,0,0,1,11.87,3.89Z" />
</svg>
<h4 class="hide-on-mobile nav-list__item_title">RIBC</h4>
</div>
<a id="dashboard_btn" href="#/dashboard_page" class="nav-list__item nav-list__item--active interact"
title="open dashboard page">
<svg class="icon icon--outlined" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M13 21V11h8v10h-8zM3 13V3h8v10H3zm6-2V5H5v6h4zM3 21v-6h8v6H3zm2-2h4v-2H5v2zm10 0h4v-6h-4v6zM13 3h8v6h-8V3zm2 2v2h4V5h-4z" />
</svg>
<svg class="icon icon--filled" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z" />
</svg>
<span class="nav-list__item_title">
Dashboard
</span>
</a>
<a id="update_panel_btn" href="#/updates_page" class="nav-list__item interact" title="show updates">
<svg class="icon icon--outlined" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M20 17h2v2H2v-2h2v-7a8 8 0 1 1 16 0v7zm-2 0v-7a6 6 0 1 0-12 0v7h12zm-9 4h6v2H9v-2z" />
</svg>
<svg class="icon icon--filled" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M20 17h2v2H2v-2h2v-7a8 8 0 1 1 16 0v7zM9 21h6v2H9v-2z" />
</svg>
<span class="nav-list__item_title">
Updates
</span>
</a>
<a href="#/applications" class="nav-list__item interact not-for-admin" title="See status of applications">
<svg class="icon icon--outlined" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24"
height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<g>
<path d="M0,0h24v24H0V0z" fill="none" />
</g>
<g>
<g>
<path
d="M15,3H5C3.9,3,3.01,3.9,3.01,5L3,19c0,1.1,0.89,2,1.99,2H19c1.1,0,2-0.9,2-2V9L15,3z M5,19V5h9v5h5v9H5z M9,8 c0,0.55-0.45,1-1,1S7,8.55,7,8s0.45-1,1-1S9,7.45,9,8z M9,12c0,0.55-0.45,1-1,1s-1-0.45-1-1s0.45-1,1-1S9,11.45,9,12z M9,16 c0,0.55-0.45,1-1,1s-1-0.45-1-1s0.45-1,1-1S9,15.45,9,16z" />
</g>
</g>
</svg>
<svg class="icon icon--filled" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24"
height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<g>
<path d="M0,0h24v24H0V0z" fill="none" />
</g>
<g>
<g>
<path
d="M15,3H5C3.9,3,3.01,3.9,3.01,5L3,19c0,1.1,0.89,2,1.99,2H19c1.1,0,2-0.9,2-2V9L15,3z M8,17c-0.55,0-1-0.45-1-1s0.45-1,1-1 s1,0.45,1,1S8.55,17,8,17z M8,13c-0.55,0-1-0.45-1-1s0.45-1,1-1s1,0.45,1,1S8.55,13,8,13z M8,9C7.45,9,7,8.55,7,8s0.45-1,1-1 s1,0.45,1,1S8.55,9,8,9z M14,10V4.5l5.5,5.5H14z" />
</g>
</g>
</svg>
<span class="nav-list__item_title">
Applications
</span>
</a>
<a href="#/admin_page" class="admin-option nav-list__item interact open-first-project"
title="open admin panel">
<svg class="icon icon--outlined" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 14v2a6 6 0 0 0-6 6H4a8 8 0 0 1 8-8zm0-1c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm0-2c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm2.595 7.812a3.51 3.51 0 0 1 0-1.623l-.992-.573 1-1.732.992.573A3.496 3.496 0 0 1 17 14.645V13.5h2v1.145c.532.158 1.012.44 1.405.812l.992-.573 1 1.732-.992.573a3.51 3.51 0 0 1 0 1.622l.992.573-1 1.732-.992-.573a3.496 3.496 0 0 1-1.405.812V22.5h-2v-1.145a3.496 3.496 0 0 1-1.405-.812l-.992.573-1-1.732.992-.572zM18 19.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z" />
</svg>
<svg class="icon icon--filled" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 14v8H4a8 8 0 0 1 8-8zm0-1c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm2.595 5.812a3.51 3.51 0 0 1 0-1.623l-.992-.573 1-1.732.992.573A3.496 3.496 0 0 1 17 14.645V13.5h2v1.145c.532.158 1.012.44 1.405.812l.992-.573 1 1.732-.992.573a3.51 3.51 0 0 1 0 1.622l.992.573-1 1.732-.992-.573a3.496 3.496 0 0 1-1.405.812V22.5h-2v-1.145a3.496 3.496 0 0 1-1.405-.812l-.992.573-1-1.732.992-.572zM18 17a1 1 0 1 0 0 2 1 1 0 0 0 0-2z" />
</svg>
<span class="nav-list__item_title">
Manage
</span>
</a>
<a id="settings_btn" href="#/settings_page" class="nav-list__item interact" title="open settings page">
<svg class="icon icon--outlined" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M2 12c0-.865.11-1.703.316-2.504A3 3 0 0 0 4.99 4.867a9.99 9.99 0 0 1 4.335-2.505 3 3 0 0 0 5.348 0 9.99 9.99 0 0 1 4.335 2.505 3 3 0 0 0 2.675 4.63c.206.8.316 1.638.316 2.503 0 .865-.11 1.703-.316 2.504a3 3 0 0 0-2.675 4.629 9.99 9.99 0 0 1-4.335 2.505 3 3 0 0 0-5.348 0 9.99 9.99 0 0 1-4.335-2.505 3 3 0 0 0-2.675-4.63C2.11 13.704 2 12.866 2 12zm4.804 3c.63 1.091.81 2.346.564 3.524.408.29.842.541 1.297.75A4.993 4.993 0 0 1 12 18c1.26 0 2.438.471 3.335 1.274.455-.209.889-.46 1.297-.75A4.993 4.993 0 0 1 17.196 15a4.993 4.993 0 0 1 2.77-2.25 8.126 8.126 0 0 0 0-1.5A4.993 4.993 0 0 1 17.195 9a4.993 4.993 0 0 1-.564-3.524 7.989 7.989 0 0 0-1.297-.75A4.993 4.993 0 0 1 12 6a4.993 4.993 0 0 1-3.335-1.274 7.99 7.99 0 0 0-1.297.75A4.993 4.993 0 0 1 6.804 9a4.993 4.993 0 0 1-2.77 2.25 8.126 8.126 0 0 0 0 1.5A4.993 4.993 0 0 1 6.805 15zM12 15a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" />
</svg>
<svg class="icon icon--filled" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M5.334 4.545a9.99 9.99 0 0 1 3.542-2.048A3.993 3.993 0 0 0 12 3.999a3.993 3.993 0 0 0 3.124-1.502 9.99 9.99 0 0 1 3.542 2.048 3.993 3.993 0 0 0 .262 3.454 3.993 3.993 0 0 0 2.863 1.955 10.043 10.043 0 0 1 0 4.09c-1.16.178-2.23.86-2.863 1.955a3.993 3.993 0 0 0-.262 3.455 9.99 9.99 0 0 1-3.542 2.047A3.993 3.993 0 0 0 12 20a3.993 3.993 0 0 0-3.124 1.502 9.99 9.99 0 0 1-3.542-2.047 3.993 3.993 0 0 0-.262-3.455 3.993 3.993 0 0 0-2.863-1.954 10.043 10.043 0 0 1 0-4.091 3.993 3.993 0 0 0 2.863-1.955 3.993 3.993 0 0 0 .262-3.454zM13.5 14.597a3 3 0 1 0-3-5.196 3 3 0 0 0 3 5.196z" />
</svg>
<span class="nav-list__item_title">
Settings
</span>
</a>
<theme-toggle class="hide-on-mobile"></theme-toggle>
</nav>
<article id="sub_page_container">
<section id="dashboard_page" class="inner-page">
<div class="flex flex-direction-column gap-2">
<div id="application_card" class="card flex align-center space-between general-only hidden">
<div>
<h2 class="margin-bottom-0-5">Looking for an internship?</h2>
<p class="margin-bottom-1">Apply for internships at RanchiMall</p>
<button class="button button--primary"
onclick="openPopup('apply_for_internship_popup')">Apply
Now</button>
</div>
<svg class="illustration" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg"
width="753.87469" height="703.82827" viewBox="0 0 753.87469 703.82827"
xmlns:xlink="http://www.w3.org/1999/xlink">
<path
d="M578.47505,103.95771l-23.06843,12.58664L271.19846,271.61447,248.13,284.2011a48.1793,48.1793,0,0,0-19.1955,65.29607L440.57765,737.39072a48.17922,48.17922,0,0,0,65.296,19.19561l.05958-.03251L836.15907,576.37545l.05958-.03251a48.17924,48.17924,0,0,0,19.19553-65.296L643.77106,123.15338A48.17929,48.17929,0,0,0,578.47505,103.95771Z"
transform="translate(-223.06266 -98.08587)" fill="#f2f2f2" />
<path
d="M585.11115,116.11916l-27.323,14.908L282.08828,281.455,254.7657,296.36278a34.30947,34.30947,0,0,0-13.66965,46.4988L452.73916,730.75513a34.30947,34.30947,0,0,0,46.4988,13.66952l.05958-.0325L829.5234,564.21377l.06-.03274a34.30935,34.30935,0,0,0,13.66926-46.49851L631.60954,129.789A34.30936,34.30936,0,0,0,585.11115,116.11916Z"
transform="translate(-223.06266 -98.08587)" fill="#fff" />
<path
d="M589.66653,236.52147,466.505,303.72109a8.01411,8.01411,0,1,1-7.677-14.07012l123.16157-67.19962a8.01411,8.01411,0,1,1,7.677,14.07012Z"
transform="translate(-223.06266 -98.08587)" fill="#f2f2f2" />
<path
d="M631.641,244.43106,479.45984,327.46442a8.01411,8.01411,0,0,1-7.677-14.07012l152.18119-83.03336a8.01411,8.01411,0,1,1,7.677,14.07012Z"
transform="translate(-223.06266 -98.08587)" fill="#f2f2f2" />
<path
d="M415.87223,275.74837l-113.5479,61.95419a3.84082,3.84082,0,0,0-1.53151,5.21006L349.14436,431.53a3.84075,3.84075,0,0,0,5.21,1.5317l113.5479-61.95419a3.84075,3.84075,0,0,0,1.53153-5.21l-48.35154-88.61735A3.84081,3.84081,0,0,0,415.87223,275.74837Z"
transform="translate(-223.06266 -98.08587)" fill="#f2f2f2" />
<path
d="M650.7763,348.96263,483.723,440.11051a8.01411,8.01411,0,1,1-7.677-14.07012l167.05327-91.14788a8.01411,8.01411,0,1,1,7.677,14.07012Z"
transform="translate(-223.06266 -98.08587)" fill="#f2f2f2" />
<path
d="M692.7508,356.87223,496.67791,463.85384a8.01411,8.01411,0,0,1-7.677-14.07012L685.07384,342.80211a8.01411,8.01411,0,1,1,7.677,14.07012Z"
transform="translate(-223.06266 -98.08587)" fill="#f2f2f2" />
<circle cx="197.03853" cy="382.67177" r="34" fill="#f2f2f2" />
<path
d="M928.81234,263.78816H552.494a48.17927,48.17927,0,0,0-48.125,48.12512V753.78907a48.17922,48.17922,0,0,0,48.125,48.12506H928.81234a48.17922,48.17922,0,0,0,48.125-48.12506V311.91328A48.17927,48.17927,0,0,0,928.81234,263.78816Z"
transform="translate(-223.06266 -98.08587)" fill="#e6e6e6" />
<path
d="M928.81283,277.64235H552.494a34.30947,34.30947,0,0,0-34.271,34.27093V753.78907A34.30947,34.30947,0,0,0,552.494,788.06H928.81283a34.30936,34.30936,0,0,0,34.27051-34.27088V311.91328A34.30937,34.30937,0,0,0,928.81283,277.64235Z"
transform="translate(-223.06266 -98.08587)" fill="#fff" />
<path
d="M875.14319,385.51745H734.84151a8.01411,8.01411,0,0,1,0-16.02823H875.14319a8.01412,8.01412,0,1,1,0,16.02823Z"
transform="translate(-223.06266 -98.08587)" fill="#6c63ff" />
<path
d="M908.20141,412.56508H734.84151a8.01411,8.01411,0,1,1,0-16.02822h173.3599a8.01411,8.01411,0,0,1,0,16.02822Z"
transform="translate(-223.06266 -98.08587)" fill="#6c63ff" />
<path
d="M703.79234,336.71073H574.44224a3.8408,3.8408,0,0,0-3.83984,3.84v100.95a3.84075,3.84075,0,0,0,3.83984,3.84h129.3501a3.84076,3.84076,0,0,0,3.83984-3.84v-100.95A3.84081,3.84081,0,0,0,703.79234,336.71073Z"
transform="translate(-223.06266 -98.08587)" fill="#e6e6e6" />
<path
d="M609.92406,398.70111a34.087,34.087,0,0,1-8.804,23.076c-5.656,6.20712-14.07618,10.3236-22.57327,8.62043-7.82416-1.56829-14.18219-8.4067-13.389-16.6795a12.356,12.356,0,0,1,15.2668-11.09515c7.43265,1.92885,10.39415,12.64095,4.20051,17.669-1.4862,1.2065-3.62136-.90359-2.12132-2.12132,4.0944-3.32385,2.8295-10.5954-2.11244-12.419-5.75371-2.12311-11.84978,2.44324-12.26355,8.32554-.49057,6.97428,4.85221,12.22646,11.40422,13.463,7.08789,1.3377,14.11532-2.29,18.91808-7.29718a30.95507,30.95507,0,0,0,8.474-21.54183,1.5009,1.5009,0,0,1,3,0Z"
transform="translate(-223.06266 -98.08587)" fill="#2f2e41" />
<circle cx="416.15529" cy="266.1673" r="53.51916" fill="#6c63ff" />
<path
d="M636.47981,387.08916l-.05566-2c3.7207-.10352,7.001-.33692,9.46582-2.1377a6.14794,6.14794,0,0,0,2.38134-4.52832,3.51432,3.51432,0,0,0-1.15283-2.89453c-1.63623-1.38184-4.269-.93457-6.188-.05469l-1.65478.75879,3.17334-23.19043,1.98144.27149-2.69922,19.72656c2.60743-.7666,5.02344-.43652,6.67823.96094a5.471,5.471,0,0,1,1.86035,4.49218,8.13264,8.13264,0,0,1-3.2002,6.07325C643.90266,386.88115,639.78694,386.99638,636.47981,387.08916Z"
transform="translate(-223.06266 -98.08587)" fill="#2f2e41" />
<rect x="431.16715" y="256.92907" width="10.77148" height="2" fill="#2f2e41" />
<rect x="397.16715" y="256.92907" width="10.77148" height="2" fill="#2f2e41" />
<path
d="M609.57212,445.34074a53.00636,53.00636,0,0,1,12.89014-5.93,8.56789,8.56789,0,0,1,.02-4.71,9.42609,9.42609,0,0,1,9.12988-6.63h13.04a9.45955,9.45955,0,0,1,9.15039,6.64,8.532,8.532,0,0,1,.01953,4.7,53.16732,53.16732,0,0,1,12.89014,5.93Z"
transform="translate(-223.06266 -98.08587)" fill="#2f2e41" />
<path
d="M700.52232,344.39072a11.57143,11.57143,0,0,0-3.52979-2.87,8.36739,8.36739,0,0,0-3.8501-.95,8.77158,8.77158,0,0,0-5.10986,1.72c-4.07031,2.88-6.89014,9.09-6.89014,16.28,0,9.02,4.43995,16.5,10.21,17.80005a8.25321,8.25321,0,0,0,1.79.2c6.60987,0,12-8.07,12-18C705.14243,352.81077,703.33238,347.68076,700.52232,344.39072Z"
transform="translate(-223.06266 -98.08587)" fill="#3f3d56" />
<path
d="M590.6024,341.86076h-.00977a8.57836,8.57836,0,0,0-4.4502-1.29,8.36738,8.36738,0,0,0-3.85009.95,11.57143,11.57143,0,0,0-3.52979,2.87l-.01025.01c-2.79981,3.29-4.60987,8.42-4.60987,14.17,0,7.76,3.27979,14.38,7.87989,16.91a8.54175,8.54175,0,0,0,4.12011,1.09,7.72431,7.72431,0,0,0,.96-.06h.00976c6.16016-.74,11.03027-8.5,11.03027-17.94C598.14243,351.01072,595.01255,344.52073,590.6024,341.86076Z"
transform="translate(-223.06266 -98.08587)" fill="#3f3d56" />
<path
d="M582.77242,373.76a1.50127,1.50127,0,0,0,1.42151-1.98,58.49864,58.49864,0,1,1,112.68726-6.5747,1.50006,1.50006,0,0,0,2.93554.61914A61.50091,61.50091,0,1,0,581.35116,372.739,1.50077,1.50077,0,0,0,582.77242,373.76Z"
transform="translate(-223.06266 -98.08587)" fill="#3f3d56" />
<path
d="M666.10324,329.57746c2.11924,2.89278,1.07447,6.79121-1.15837,9.28528-2.90548,3.24541-7.53877,3.45016-11.5618,2.8478-4.51431-.67591-9.3026-2.7909-13.87293-1.3656-3.89537,1.2148-6.67418,4.74793-10.7211,5.63537-3.589.787-7.88081-.25477-9.139-4.08016-.60459-1.83823,2.29142-2.6261,2.89284-.79751.81395,2.47478,4.32865,2.42543,6.34145,1.74012,3.22689-1.09867,5.71374-3.77105,8.8854-5.04749,3.73933-1.50491,7.79621-.82549,11.60323.03181,3.58831.808,7.718,2.006,11.29267.49665,2.64515-1.1169,4.74985-4.635,2.84717-7.23211-1.14219-1.5591,1.45985-3.05738,2.59042-1.51416Z"
transform="translate(-223.06266 -98.08587)" fill="#2f2e41" />
<path
d="M874.932,513.49157H684.63034a8.01411,8.01411,0,1,1,0-16.02823H874.932a8.01412,8.01412,0,0,1,0,16.02823Z"
transform="translate(-223.06266 -98.08587)" fill="#e6e6e6" />
<path
d="M907.99023,540.5392H684.63034a8.01412,8.01412,0,1,1,0-16.02823H907.99023a8.01412,8.01412,0,1,1,0,16.02823Z"
transform="translate(-223.06266 -98.08587)" fill="#e6e6e6" />
<path
d="M874.932,610.705H684.63034a8.01411,8.01411,0,1,1,0-16.02822H874.932a8.01411,8.01411,0,1,1,0,16.02822Z"
transform="translate(-223.06266 -98.08587)" fill="#e6e6e6" />
<path
d="M907.99023,637.75267H684.63034a8.01411,8.01411,0,1,1,0-16.02823H907.99023a8.01411,8.01411,0,1,1,0,16.02823Z"
transform="translate(-223.06266 -98.08587)" fill="#e6e6e6" />
<circle cx="386.2497" cy="420.61448" r="34" fill="#e6e6e6" />
<circle cx="386.2497" cy="518.61448" r="34" fill="#e6e6e6" />
<path
d="M874.932,708.705H684.63034a8.01411,8.01411,0,1,1,0-16.02822H874.932a8.01411,8.01411,0,1,1,0,16.02822Z"
transform="translate(-223.06266 -98.08587)" fill="#e6e6e6" />
<path
d="M907.99023,735.75267H684.63034a8.01411,8.01411,0,1,1,0-16.02823H907.99023a8.01411,8.01411,0,1,1,0,16.02823Z"
transform="translate(-223.06266 -98.08587)" fill="#e6e6e6" />
<circle cx="386.2497" cy="616.61448" r="34" fill="#e6e6e6" />
</svg>
</div>
<section id="pinned_project_section" class="w-100">
<h4>Pinned</h4>
<div id="pinned_projects" class="observe-empty-state"></div>
<div class="empty-state">
<h4>There are no pinned projects</h4>
<p class="margin-block-0-5">
You can pin projects for easier monitoring by clicking on the 'pin' icon on project
card.
</p>
</div>
</section>
<div id="project_list_container">
<div class="flex align-center space-between margin-bottom-0-5">
<h4>Projects</h4>
<a href="#/project_explorer" class="button open-first-project">All</a>
</div>
<div id="project_list" class="flex flex-direction-column gap-0-3"></div>
</div>
<section id="intern_view" class="hidden intern-option">
<h2>My tasks</h2>
<ul id="assigned_task_list"></ul>
</section>
</div>
<div id="best_interns_container" class="container-card">
<div class="container-header">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 7a8 8 0 1 1 0 16 8 8 0 0 1 0-16zm0 3.5l-1.323 2.68-2.957.43 2.14 2.085-.505 2.946L12 17.25l2.645 1.39-.505-2.945 2.14-2.086-2.957-.43L12 10.5zm1-8.501L18 2v3l-1.363 1.138A9.935 9.935 0 0 0 13 5.049L13 2zm-2 0v3.05a9.935 9.935 0 0 0-3.636 1.088L6 5V2l5-.001z" />
</svg>
<h4>Leaderboard</h4>
<a id="all_interns_btn" href="#/all_interns_page" class="button">All</a>
</div>
<div id="top_interns"></div>
</div>
</section>
<section id="admin_page" class="inner-page hidden">
<section id="admin_page__left" class="flex flex-direction-column">
<strip-select id="admin_view_selector">
<strip-option value="0" selected>Projects</strip-option>
<strip-option value="1">Interns</strip-option>
<strip-option value="2">Requests</strip-option>
</strip-select>
<div id="admin_views">
<section id="projects_container" class="flex flex-direction-column">
<button class="button button--small margin-left-auto margin-block-0-5"
onclick="openPopup('add_project_popup')">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z"></path>
<path
d="M12.414 5H21a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7.414l2 2zM4 5v14h16V7h-8.414l-2-2H4zm7 7V9h2v3h3v2h-3v3h-2v-3H8v-2h3z">
</path>
</svg>
Add project
</button>
<div id="admin_page__project_list" class="list-container observe-empty-state"></div>
<h4 class="empty-state">No project added</h4>
</section>
<section id="interns_container" class="flex flex-direction-column hidden">
<button class="button button--small margin-left-auto margin-block-0-5"
onclick="openPopup('add_intern_popup')">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M14 14.252v2.09A6 6 0 0 0 6 22l-2-.001a8 8 0 0 1 10-7.748zM12 13c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm0-2c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm6 6v-3h2v3h3v2h-3v3h-2v-3h-3v-2h3z" />
</svg>
Add intern
</button>
<ul id="admin_page__intern_list" class="list-container observe-empty-state"></ul>
<h4 class="empty-state">No interns added</h4>
</section>
<section class="flex flex-direction-column hidden">
<strip-select id="request_type_selector" class="margin-left-auto margin-block-0-5">
<strip-option value="task" selected>Task</strip-option>
<strip-option value="intern">Internship</strip-option>
</strip-select>
<ul id="requests_list" class="list-container observe-empty-state"></ul>
<h4 class="empty-state">No pending requests</h4>
</section>
</div>
</section>
<section id="project_editing_panel" class="hidden">
<div id="project_details_wrapper"
class="flex flex-direction-column gap-1 margin-bottom-2 align-items-start">
<a class="button icon-only hide-on-desktop" href="#/admin_page">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none"></path>
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></path>
</svg>
</a>
<div class="flex flex-wrap align-items-start">
<h2 id="editing_panel__title" data-editable></h2>
<button class="button button--small button--transparent admin-option"
title="Edit this title" onclick="makeEditable(this.previousElementSibling)">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M14.06 9.02l.92.92L5.92 19H5v-.92l9.06-9.06M17.66 3c-.25 0-.51.1-.7.29l-1.83 1.83 3.75 3.75 1.83-1.83c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.2-.2-.45-.29-.71-.29zm-3.6 3.19L3 17.25V21h3.75L17.81 9.94l-3.75-3.75z" />
</svg>
</button>
</div>
<div class="flex flex-wrap align-items-start">
<p id="editing_panel__description" data-editable></p>
<button class="button button--small button--transparent admin-option"
title="Edit this description" onclick="makeEditable(this.previousElementSibling)">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M14.06 9.02l.92.92L5.92 19H5v-.92l9.06-9.06M17.66 3c-.25 0-.51.1-.7.29l-1.83 1.83 3.75 3.75 1.83-1.83c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.2-.2-.45-.29-.71-.29zm-3.6 3.19L3 17.25V21h3.75L17.81 9.94l-3.75-3.75z" />
</svg>
</button>
</div>
</div>
<div class="grid gap-0-3">
<h4>Branches</h4>
<div id="branch_container"></div>
</div>
<h4>Tasks</h4>
<ul id="task_list" class="grid observe-empty-state"></ul>
<h4 class="empty-state padding-block-1">No tasks added yet, tasks will appear here after adding
them.</h4>
<sm-button id="add_task" title="show element to add new task" onclick="addPlaceholderTask()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z" />
</svg>
Add task
</sm-button>
<ul id="task_context" class="hidden">
<li tabindex="0" class="interact" onclick="toggleEditing('title')">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M15.728 9.686l-1.414-1.414L5 17.586V19h1.414l9.314-9.314zm1.414-1.414l1.414-1.414-1.414-1.414-1.414 1.414 1.414 1.414zM7.242 21H3v-4.243L16.435 3.322a1 1 0 0 1 1.414 0l2.829 2.829a1 1 0 0 1 0 1.414L7.243 21z" />
</svg>
Edit title
</li>
<li tabindex="0" class="interact" onclick="toggleEditing()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M15.728 9.686l-1.414-1.414L5 17.586V19h1.414l9.314-9.314zm1.414-1.414l1.414-1.414-1.414-1.414-1.414 1.414 1.414 1.414zM7.242 21H3v-4.243L16.435 3.322a1 1 0 0 1 1.414 0l2.829 2.829a1 1 0 0 1 0 1.414L7.243 21z" />
</svg>
Edit description
</li>
<li onclick="showNewBranchPopup()" tabindex="0" class="interact">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M7.105 15.21A3.001 3.001 0 1 1 5 15.17V8.83a3.001 3.001 0 1 1 2 0V12c.836-.628 1.874-1 3-1h4a3.001 3.001 0 0 0 2.895-2.21 3.001 3.001 0 1 1 2.032.064A5.001 5.001 0 0 1 14 13h-4a3.001 3.001 0 0 0-2.895 2.21zM6 17a1 1 0 1 0 0 2 1 1 0 0 0 0-2zM6 5a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm12 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2z" />
</svg>
Create new Branch
</li>
</ul>
</section>
<button class="fab admin-option" onclick="commitToChanges()">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M7 19v-6h10v6h2V7.828L16.172 5H5v14h2zM4 3h13l4 4v13a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm5 12v4h6v-4H9z" />
</svg>
Commit changes
</button>
</section>
<section id="updates_page" class="inner-page hidden">
<button class="button hide-on-desktop justify-self-end" onclick="toggleFilter()">Filter</button>
<section id="update_filters_wrapper" class="grid hide-on-mobile">
<h4>Filter</h4>
<div class="grid gap-0-5">
<h5 class="uppercase">By Projects</h5>
<sm-select id="updates_page__project_selector"></sm-select>
</div>
<div class="grid gap-0-5">
<h5 class="uppercase">By Intern</h5>
<sm-select id="updates_page__intern_selector"></sm-select>
</div>
<label class="grid gap-0-5">
<h5 class="uppercase">By date</h5>
<input type="date" id="updates_page__date_selector" aria-label="Filter updates by date"
min="2020-01-01">
</label>
<button class="button" onclick="clearFilter()">Clear</button>
</section>
<section id="updates_wrapper">
<ul id="all_updates_list" class="grid gap-0-5 observe-empty-state"></ul>
<h4 class="empty-state">No related updates</h4>
</section>
</section>
<section id="applications" class="inner-page hidden align-content-start">
<p>Check status of your applications</p>
<div class="grid gap-0-5 hidden">
<h4>Task applications</h4>
<ul id="task_requests_list" class="grid gap-0-5 observe-empty-state"></ul>
<h4 class="empty-state">No task requests</h4>
</div>
<div class="grid gap-0-5">
<h4>Internship applications</h4>
<ul id="internship_requests_list" class="grid gap-0-5 observe-empty-state"></ul>
<h4 class="empty-state">No applications</h4>
</div>
</section>
<section id="all_interns_page" class="inner-page hidden flex flex-direction-column align-start">
<div id="all_interns_page__header" class="grid gap-0-5">
<h2>Interns</h2>
<sm-input id="interns_page__search" placeholder="Search">
<svg class="icon search__icon" slot="icon" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15z" />
</svg>
</sm-input>
</div>
<ul id="all_interns_list" class="grid observe-empty-state"></ul>
<h4 class="empty-state">No intern found</h4>
</section>
<section id="project_explorer" class="inner-page hidden">
<div id="project_explorer__left" class="list-container">
<h4 class="padding intern-option hidden">My projects</h4>
<div id="my_projects"></div>
<h4 class="padding intern-option hidden">Other projects</h4>
<div id="other_projects"></div>
</div>
<section id="project_explorer__right" class="grid hide-on-mobile">
<header class="flex flex-direction-column gap-0-5 align-items-start">
<a class="button icon-only hide-on-desktop" href="#/project_explorer">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none"></path>
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></path>
</svg>
</a>
<h2 id="project_explorer__project_title"></h2>
</header>
<p id="project_explorer__project_description"></p>
<a href="" id="project_explorer__project_updates"
class="button button--small margin-right-auto">Check
related updates</a>
<div id="explorer_branch_container" class="flex align-center flex-wrap gap-0-3 margin-top-0-5">
</div>
<div id="explorer_task_list" class="observe-empty-state"></div>
<h4 class="empty-state">No tasks are added to this projects</h4>
</section>
</section>
<section id="settings_page" class="inner-page hidden">
<section class="grid gap-1 card">
<div class="grid">
<h4>FLO address</h4>
<sm-copy id="user_flo_id"></sm-copy>
</div>
<div class="flex gap-0-5 flex-wrap">
<button class="button" onclick="downloadCredentials()">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg"
enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<path
d="M18,15v3H6v-3H4v3c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-3H18z M17,11l-1.41-1.41L13,12.17V4h-2v8.17L8.41,9.59L7,11l5,5 L17,11z" />
</g>
</svg>
Download credentials
</button>
<button id="logout" class="justify-self-start button button--danger" onclick="signOut()">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M4 18h2v2h12V4H6v2H4V3a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-3zm2-7h7v2H6v3l-5-4 5-4v3z" />
</svg>
Sign out
</button>
</div>
</section>
<div class="grid gap-1 card">
<div class="grid gap-0-5">
<h4>Secure private key</h4>
<p>
You can set a password to secure your private key and use the password instead of private
key.
This is applied to this browser only.
</p>
</div>
<button id="secure_pwd_button" class="button button--primary justify-self-start secure-priv-key"
onclick="openPopup('secure_pwd_popup')">Set
password</button>
</div>
</section>
</article>
</main>
</main>
<sm-popup id="intern_info_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="closePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
</header>
<section class="grid gap-1">
<div id="intern_info__initials" class="intern-card__initials"></div>
<h3 id="intern_info__name">Intern name</h3>
<sm-copy id="intern_info__flo_id"></sm-copy>
<div id="update_intern_score" class="flex align-center space-between">
<div class="flex">
<svg class="icon gold-fill margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 18.26l-7.053 3.948 1.575-7.928L.587 8.792l8.027-.952L12 .5l3.386 7.34 8.027.952-5.935 5.488 1.575 7.928z" />
</svg>
<h4 id="intern_info__score"></h4>
</div>
<div class="flex admin-option hidden">
<sm-button id="reduce_score_button" title="increase intern score" onclick="changeScore(-1)">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M5 11h14v2H5z" />
</svg>
</sm-button>
<sm-button id="increase_score_button" title="decrease intern score" onclick="changeScore(1)">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z" />
</svg>
</sm-button>
</div>
</div>
</section>
</sm-popup>
<sm-popup id="intern_list_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="closePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
<h3 class="medium-block-0-5">Choose an intern</h3>
</header>
<sm-input id="intern_search_field" placeholder="Search for interns" type="search" autofocus>
<svg slot="icon" class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15z" />
</svg>
</sm-input>
<ul id="intern_list_container" class="observe-empty-state"></ul>
<h4 class="empty-state">No intern found</h4>
</sm-popup>
<sm-popup id="add_project_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="closePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
<h3>Add new project</h3>
</header>
<sm-form>
<sm-input id="project_name_field" placeholder="Name" autofocus required></sm-input>
<sm-textarea id="project_description_field" placeholder="Description" rows="4" required></sm-textarea>
<sm-button variant="primary" onclick="addProjectToList()" disabled>
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z" />
</svg>
Add
</sm-button>
</sm-form>
</sm-popup>
<sm-popup id="add_intern_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="closePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
<h3>Add new intern</h3>
</header>
<sm-form>
<sm-input id="intern_name_field" placeholder="Name" required></sm-input>
<sm-input id="intern_flo_id_field" placeholder="FLO address" error-text="Invalid FLO address" data-flo-id
required>
</sm-input>
<sm-button variant="primary" onclick="addInternToList()" disabled>
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z" />
</svg>
Add
</sm-button>
</sm-form>
</sm-popup>
<sm-popup id="create_branch_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="closePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
<h3>Create a new branch</h3>
</header>
<sm-form>
<sm-input id="branch_start_point" placeholder="Start point" type="number" required></sm-input>
<sm-input id="branch_merge_point" placeholder="Merge point" type="number"></sm-input>
<sm-button id="create_branch_btn" variant="primary">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z" />
</svg>
Create
</sm-button>
</sm-form>
</sm-popup>
<sm-popup id="post_update_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="closePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
<h3>Post an update</h3>
</header>
<h5 id="update_of_project"></h5>
<h3 id="update_of_task"></h3>
<sm-form>
<sm-textarea id="update__brief" placeholder="Type the update" rows="4" autofocus required>
</sm-textarea>
<sm-input id="update__link" placeholder="Related link (optional)" animate></sm-input>
<sm-button id="post_update_btn" title="post this update" variant="primary" disabled onclick="postUpdate()">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z"></path>
<path
d="M1.946 9.315c-.522-.174-.527-.455.01-.634l19.087-6.362c.529-.176.832.12.684.638l-5.454 19.086c-.15.529-.455.547-.679.045L12 14l6-8-8 6-8.054-2.685z">
</path>
</svg>
Post update
</sm-button>
</sm-form>
</sm-popup>
<sm-popup id="apply_for_internship_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="closePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
<h3>Apply for internship</h3>
</header>
<sm-form>
<sm-input id="intern_apply__name" placeholder="Full name" autofocus required animate></sm-input>
<sm-textarea id="intern_apply__brief" placeholder="Tell us about yourself" rows="6" required></sm-textarea>
<sm-input id="intern_apply__link" placeholder="Link to your portfolio (optional)" animate></sm-input>
<div class="multi-state-button">
<sm-button id="intern_apply__button" title="post this update" variant="primary" disabled
onclick="applyForInternship()">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M1.923 9.37c-.51-.205-.504-.51.034-.689l19.086-6.362c.529-.176.832.12.684.638l-5.454 19.086c-.15.529-.475.553-.717.07L11 13 1.923 9.37zm4.89-.2l5.636 2.255 3.04 6.082 3.546-12.41L6.812 9.17z" />
</svg>
Apply
</sm-button>
</div>
</sm-form>
</sm-popup>
<sm-popup id="secure_pwd_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close justify-self-start">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
</svg>
</button>
<h3 id="secure_pwd_title">Set password</h3>
</header>
<sm-form>
<sm-input id="secure_pwd_input" class="password-field" type="password" placeholder="Password" animate
required autofocus>
<label slot="right" class="interact">
<input type="checkbox" class="hidden" autocomplete="off" readonly
onchange="togglePrivateKeyVisibility(this)">
<svg class="icon invisible" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<title>Hide password</title>
<path d="M0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0z" fill="none" />
<path
d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z" />
</svg>
<svg class="icon visible" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<title>Show password</title>
<path d="M0 0h24v24H0z" fill="none" />
<path
d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" />
</svg>
</label>
</sm-input>
<button class="button button--primary cta secure-priv-key" type="submit" onclick="setSecurePassword()">
Set
</button>
</sm-form>
</sm-popup>
<!-- Templates -->
<template id="placeholder_task_card_template">
<div class="temp-task grid gap-0-5">
<sm-input class="placeholder-task__title" placeholder="Title"></sm-input>
<sm-textarea class="placeholder-task__description" placeholder="Description" rows="4"></sm-textarea>
<div class="flex">
<sm-button class="cancel-task-button">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
Cancel
</sm-button>
<sm-button class="add-task-button">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z" />
</svg>
Add
</sm-button>
</div>
</div>
</template>
<script>
/*jshint esversion: 8 */
/**
* @yaireo/relative-time - javascript function to transform timestamp or date to local relative-time
*
* @version v1.0.0
* @homepage https://github.com/yairEO/relative-time
*/
!function (e, t) { var o = o || {}; "function" == typeof o && o.amd ? o([], t) : "object" == typeof exports && "object" == typeof module ? module.exports = t() : "object" == typeof exports ? exports.RelativeTime = t() : e.RelativeTime = t() }(this, (function () { const e = { year: 31536e6, month: 2628e6, day: 864e5, hour: 36e5, minute: 6e4, second: 1e3 }, t = "en", o = { numeric: "auto" }; function n(e) { e = { locale: (e = e || {}).locale || t, options: { ...o, ...e.options } }, this.rtf = new Intl.RelativeTimeFormat(e.locale, e.options) } return n.prototype = { from(t, o) { const n = t - (o || new Date); for (let t in e) if (Math.abs(n) > e[t] || "second" == t) return this.rtf.format(Math.round(n / e[t]), t) } }, n }));
const relativeTime = new RelativeTime({ style: 'narrow' });
</script>
<script id="default_ui_library">
// Global variables
const { html, render: renderElem } = uhtml;
const domRefs = {}
//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', '', true)
window.addEventListener('offline', () => {
notify('There seems to be a problem connecting to the internet, Please check you internet connection.', 'error', true, 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);
};
}
let zIndex = 50
// function required for popups or modals to appear
function openPopup(popupId, pinned) {
zIndex++
getRef(popupId).setAttribute('style', `z-index: ${zIndex}`)
getRef(popupId).show({ pinned })
return getRef(popupId);
}
// hides the popup or modal
function closePopup() {
if (popupStack.peek() === undefined)
return;
popupStack.peek().popup.hide()
}
// displays a popup for asking permission. Use this instead of JS confirm
const getConfirmation = (title, options = {}) => {
return new Promise(resolve => {
const { message = '', cancelText = 'Cancel', confirmText = 'OK' } = options
openPopup('confirmation_popup', true)
getRef('confirm_title').innerText = title;
getRef('confirm_message').innerText = message;
const cancelButton = getRef('confirmation_popup').querySelector('.cancel-button');
const confirmButton = getRef('confirmation_popup').querySelector('.confirm-button')
confirmButton.textContent = confirmText
cancelButton.textContent = cancelText
confirmButton.onclick = () => {
closePopup()
resolve(true);
}
cancelButton.onclick = () => {
closePopup()
resolve(false);
}
})
}
//Function for displaying toast notifications. pass in error for mode param if you want to show an error.
function notify(message, mode, options = {}) {
let icon
switch (mode) {
case 'success':
icon = `<svg class="icon icon--success" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg>`
break;
case 'error':
icon = `<svg class="icon icon--error" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm0-8v6h2V7h-2z"/></svg>`
options.pinned = true
break;
}
getRef("notification_drawer").push(message, { icon, ...options });
if (mode === 'error') {
console.error(message)
}
}
// detect browser version
function detectBrowser() {
let ua = navigator.userAgent,
tem,
M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if (/trident/i.test(M[1])) {
tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
return 'IE ' + (tem[1] || '');
}
if (M[1] === 'Chrome') {
tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
if (tem != null) return tem.slice(1).join(' ').replace('OPR', 'Opera');
}
M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
if ((tem = ua.match(/version\/(\d+)/i)) != null) M.splice(1, 1, tem[1]);
return M.join(' ');
}
window.addEventListener('hashchange', e => routeTo(window.location.hash))
window.addEventListener("load", () => {
const [browserName, browserVersion] = detectBrowser().split(' ');
const supportedVersions = {
Chrome: 85,
Firefox: 75,
Safari: 13,
}
if (browserName in supportedVersions) {
if (parseInt(browserVersion) < supportedVersions[browserName]) {
notify(`${browserName} ${browserVersion} is not fully supported, some features may not work properly. Please update to ${supportedVersions[browserName]} or higher.`, 'error')
}
} else {
notify('Browser is not fully compatible, some features may not work. for best experience please use Chrome, Edge, Firefox or Safari', 'error')
}
document.body.classList.remove('hidden')
document.querySelectorAll('sm-input[data-flo-id]').forEach(input => input.customValidation = floCrypto.validateAddr)
document.querySelectorAll('sm-input[data-private-key]').forEach(input => input.customValidation = floCrypto.getPubKeyHex)
document.addEventListener('keyup', (e) => {
if (e.code === 'Escape') {
closePopup()
}
})
document.addEventListener("pointerdown", (e) => {
if (e.target.closest("button:not([disabled]), 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(4)",
opacity: 0,
},
],
{
duration: 600,
fill: "forwards",
easing: "ease-out",
}
);
target.append(circle);
rippleAnimation.onfinish = () => {
circle.remove();
};
}
function getFormattedTime(timestamp, format) {
try {
timestamp = parseInt(timestamp)
if (String(timestamp).length < 13)
timestamp *= 1000
let [day, month, date, year] = new Date(timestamp).toString().split(' '),
minutes = new Date(timestamp).getMinutes(),
hours = new Date(timestamp).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`
switch (format) {
case 'date-only':
return `${month} ${date}, ${year}`;
break;
case 'time-only':
return finalHours;
case 'relative':
// check if timestamp is older than a day
if (Date.now() - new Date(timestamp) < 60 * 60 * 24 * 1000)
return `${finalHours}`;
else
return relativeTime.from(timestamp)
default:
return `${month} ${date}, ${year} at ${finalHours}`;
}
} catch (e) {
console.error(e);
return timestamp;
}
}
const appState = {
params: {},
}
function routeTo(targetPage, options = {}) {
const { firstLoad, hashChange } = options
let pageId
let subPageId1
let searchParams
let params
if (targetPage === '') {
try {
if (floDapps.user.id)
pageId = 'dashboard_page'
} catch (e) {
pageId = 'sign_in'
}
} else {
if (targetPage.includes('/')) {
if (targetPage.includes('?')) {
const splitAddress = targetPage.split('?')
searchParams = splitAddress.pop();
[, pageId, subPageId1] = splitAddress.pop().split('/')
} else {
[, pageId, subPageId1] = targetPage.split('/')
}
} else {
pageId = targetPage
}
}
if (!document.querySelector(`#${pageId}`)?.classList.contains('inner-page')) return
try {
if (floDapps.user.id && (['sign_up', 'sign_in', 'loading', 'landing'].includes(pageId))) {
history.replaceState(null, null, '#/dashboard_page');
pageId = 'dashboard_page'
}
} catch (e) {
if (!(['sign_up', 'sign_in', 'loading', 'landing'].includes(pageId))) return
}
appState.openPage = pageId
if (searchParams) {
const urlSearchParams = new URLSearchParams('?' + searchParams);
params = Object.fromEntries(urlSearchParams.entries());
}
if (params)
appState.params = params
switch (pageId) {
case 'sign_in':
setTimeout(() => {
getRef('private_key_field').focusIn()
}, 0);
break;
case 'sign_up':
const { floID, privKey } = floCrypto.generateNewID()
getRef('generated_flo_address').value = floID
getRef('generated_private_key').value = privKey
break;
case 'dashboard_page':
render.dashProjects(getRef('pinned_projects'), pinnedProjects);
break;
case 'updates_page':
if (!getRef('updates_page__project_selector').children.length) {
renderProjectSelectorOptions()
renderInternSelectorOptions()
}
const { projectId, internId, date } = params || getUpdateFilters()
if (params) {
setUpdateFilters({ projectId, internId, date })
} else if (projectId) {
const dateParam = date !== '' ? `&date=${date}` : ''
history.replaceState(null, null, `#/updates_page?projectId=${projectId}&internId=${internId}${dateParam}`)
}
let matchedUpdates
if (projectId !== 'all') {
matchedUpdates = getUpdatesByProject(projectId)
}
if (internId !== 'all') {
matchedUpdates = getUpdatesByIntern(internId, matchedUpdates)
}
if (date) {
matchedUpdates = getUpdatesByDate(date, matchedUpdates)
}
renderInternUpdates(matchedUpdates)
break;
case 'applications':
render.internApplications()
break;
case 'all_interns_page':
renderAllInterns()
break;
case 'project_explorer':
if (subPageId1) {
if (params) {
const { id: projectId, branch } = params
if (appState.params.projectId !== projectId) {
showProjectInfo(projectId)
const allProjects = getRef('project_explorer__left').querySelectorAll('.project-card');
allProjects.forEach(project => project.classList.remove('project-card--active'))
const targetProject = [...allProjects].find(project => project.getAttribute('href').includes(projectId))
if (targetProject)
targetProject.classList.add('project-card--active')
}
if (branch) {
renderBranchTasks()
}
getRef('project_explorer__left').classList.add('hide-on-mobile')
getRef('project_explorer__right').classList.remove('hide-on-mobile')
} else {
getRef('project_explorer__left').querySelectorAll('.project-card').forEach(project => project.classList.remove('project-card--active'))
}
} else {
getRef('project_explorer__left').classList.remove('hide-on-mobile')
getRef('project_explorer__right').classList.add('hide-on-mobile')
history.replaceState(null, '', '#/project_explorer')
}
break;
case 'admin_page':
if (subPageId1) {
if (params && RIBC.getProjectList().includes(params.id)) {
const { id: projectId, branch } = params
renderAdminProjectView(projectId)
if (branch) {
renderBranchTasks()
}
getRef('admin_page__left').classList.add('hide-on-mobile')
getRef('project_editing_panel').classList.remove('hidden')
}
} else {
getRef('admin_page__left').classList.remove('hide-on-mobile')
getRef('project_editing_panel').classList.add('hidden')
history.replaceState(null, '', '#/admin_page')
}
break;
}
if (appState.lastPage !== pageId) {
if (document.querySelector('.nav-list__item--active'))
document.querySelector('.nav-list__item--active').classList.remove('nav-list__item--active');
const targetListItem = [...document.querySelectorAll(`a.nav-list__item`)].find(item => item.href.includes(pageId))
if (targetListItem)
targetListItem.classList.add('nav-list__item--active')
document.querySelectorAll('.page').forEach(page => page.classList.add('hidden'))
getRef(pageId).closest('.page').classList.remove('hidden')
document.querySelectorAll('.inner-page').forEach(page => page.classList.add('hidden'))
getRef(pageId).classList.remove('hidden')
let ogOverflow = getRef(pageId).parentNode.style.overflow
getRef(pageId).parentNode.style.overflow = 'hidden'
getRef(pageId).animate([
{
opacity: 0,
transform: 'translateY(1rem)'
},
{
opacity: 1,
transform: 'translateY(0)'
},
],
{
duration: 300,
easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
}).onfinish = () => {
getRef(pageId).parentNode.style.overflow = ogOverflow
}
appState.lastPage = pageId
}
}
// class based lazy loading
class LazyLoader {
constructor(container, elementsToRender, renderFn, options = {}) {
const { batchSize = 10, freshRender, bottomFirst = false, domUpdated } = options
this.elementsToRender = elementsToRender
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
this.renderFn = renderFn
this.intersectionObserver
this.batchSize = batchSize
this.freshRender = freshRender
this.domUpdated = domUpdated
this.bottomFirst = bottomFirst
this.shouldLazyLoad = false
this.lastScrollTop = 0
this.lastScrollHeight = 0
this.lazyContainer = document.querySelector(container)
this.update = this.update.bind(this)
this.render = this.render.bind(this)
this.init = this.init.bind(this)
this.clear = this.clear.bind(this)
}
get elements() {
return this.arrayOfElements
}
init() {
this.intersectionObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
observer.disconnect()
this.render({ lazyLoad: true })
}
})
})
this.mutationObserver = new MutationObserver(mutationList => {
mutationList.forEach(mutation => {
if (mutation.type === 'childList') {
if (mutation.addedNodes.length) {
if (this.bottomFirst) {
if (this.lazyContainer.firstElementChild)
this.intersectionObserver.observe(this.lazyContainer.firstElementChild)
} else {
if (this.lazyContainer.lastElementChild)
this.intersectionObserver.observe(this.lazyContainer.lastElementChild)
}
}
}
})
})
this.mutationObserver.observe(this.lazyContainer, {
childList: true,
})
this.render()
}
update(elementsToRender) {
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
}
render(options = {}) {
let { lazyLoad = false } = options
this.shouldLazyLoad = lazyLoad
const frag = document.createDocumentFragment();
if (lazyLoad) {
if (this.bottomFirst) {
this.updateEndIndex = this.updateStartIndex
this.updateStartIndex = this.updateEndIndex - this.batchSize
} else {
this.updateStartIndex = this.updateEndIndex
this.updateEndIndex = this.updateEndIndex + this.batchSize
}
} else {
this.intersectionObserver.disconnect()
if (this.bottomFirst) {
this.updateEndIndex = this.arrayOfElements.length
this.updateStartIndex = this.updateEndIndex - this.batchSize - 1
} else {
this.updateStartIndex = 0
this.updateEndIndex = this.batchSize
}
this.lazyContainer.innerHTML = ``;
}
this.lastScrollHeight = this.lazyContainer.scrollHeight
this.lastScrollTop = this.lazyContainer.scrollTop
this.arrayOfElements.slice(this.updateStartIndex, this.updateEndIndex).forEach((element, index) => {
frag.append(this.renderFn(element))
})
if (this.bottomFirst) {
this.lazyContainer.prepend(frag)
// scroll anchoring for reverse scrolling
this.lastScrollTop += this.lazyContainer.scrollHeight - this.lastScrollHeight
this.lazyContainer.scrollTo({ top: this.lastScrollTop })
this.lastScrollHeight = this.lazyContainer.scrollHeight
} else {
this.lazyContainer.append(frag)
}
if (!lazyLoad && this.bottomFirst) {
this.lazyContainer.scrollTop = this.lazyContainer.scrollHeight
}
// Callback to be called if elements are updated or rendered for first time
if (!lazyLoad && this.freshRender)
this.freshRender()
}
clear() {
this.intersectionObserver.disconnect()
this.mutationObserver.disconnect()
this.lazyContainer.innerHTML = ``;
}
reset() {
this.arrayOfElements = (typeof this.elementsToRender === 'function') ? this.elementsToRender() : this.elementsToRender || []
this.render()
}
}
function buttonLoader(id, show) {
const button = typeof id === 'string' ? getRef(id) : id;
button.disabled = show;
const animOptions = {
duration: 200,
fill: 'forwards',
easing: 'ease'
}
if (show) {
button.animate([
{
clipPath: 'circle(100%)',
},
{
clipPath: 'circle(0)',
},
], animOptions).onfinish = e => {
e.target.commitStyles()
e.target.cancel()
}
button.parentNode.append(createElement('sm-spinner'))
} else {
button.style = ''
const potentialTarget = button.parentNode.querySelector('sm-spinner')
if (potentialTarget) potentialTarget.remove();
}
}
// implement event delegation
function delegate(el, event, selector, fn) {
el.addEventListener(event, function (e) {
const potentialTarget = e.target.closest(selector)
if (potentialTarget) {
e.delegateTarget = potentialTarget
fn.call(this, e)
}
})
}
const slideInLeft = [
{
opacity: 0,
transform: 'translateX(1rem)'
},
{
opacity: 1,
transform: 'translateX(0)'
}
]
const slideOutLeft = [
{
opacity: 1,
transform: 'translateX(0)'
},
{
opacity: 0,
transform: 'translateX(-1rem)'
},
]
const slideInRight = [
{
opacity: 0,
transform: 'translateX(-1rem)'
},
{
opacity: 1,
transform: 'translateX(0)'
}
]
const slideOutRight = [
{
opacity: 1,
transform: 'translateX(0)'
},
{
opacity: 0,
transform: 'translateX(1rem)'
},
]
const slideInDown = [
{
opacity: 0,
transform: 'translateY(-1rem)'
},
{
opacity: 1,
transform: 'translateY(0)'
},
]
const slideOutUp = [
{
opacity: 1,
transform: 'translateY(0)'
},
{
opacity: 0,
transform: 'translateY(-1rem)'
},
]
function showChildElement(id, index, options = {}) {
return new Promise((resolve) => {
const { mobileView = false, entry, exit } = options
const animOptions = {
duration: 150,
easing: 'ease',
fill: 'forwards'
}
const parent = typeof id === 'string' ? document.getElementById(id) : id;
const visibleElement = [...parent.children].find(elem => !elem.classList.contains(mobileView ? 'hide-on-mobile' : 'hidden'));
if (visibleElement === parent.children[index]) return;
visibleElement.getAnimations().forEach(anim => anim.cancel())
parent.children[index].getAnimations().forEach(anim => anim.cancel())
if (visibleElement) {
if (exit) {
parent.style.overflow = 'hidden'
visibleElement.animate(exit, animOptions).onfinish = () => {
visibleElement.classList.add(mobileView ? 'hide-on-mobile' : 'hidden')
parent.style.overflow = ''
}
parent.children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
if (entry)
parent.children[index].animate(entry, animOptions).onfinish = () => resolve()
} else {
visibleElement.classList.add(mobileView ? 'hide-on-mobile' : 'hidden')
parent.children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
resolve()
}
} else {
parent.children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
parent.children[index].animate(entry, animOptions).onfinish = () => resolve()
}
})
}
function togglePrivateKeyVisibility(input) {
const target = input.closest('sm-input')
target.type = target.type === 'password' ? 'text' : 'password';
target.focusIn()
}
</script>
<script id="UI_functions">
// Method object for creating various UI elements
const render = {
projectCard(projectCode, isAdmin = false, ref) {
const projectName = RIBC.getProjectDetails(projectCode).projectName
const page = isAdmin ? 'admin_page' : 'project_explorer'
return html.for(ref, projectCode)`<a class="project-card flex align-center interact" title="Project information" href=${`#/${page}/project?id=${projectCode}&branch=mainLine`}>${projectName}</a>`
},
taskCard(taskNo) {
const { taskTitle, taskDescription } = RIBC.getTaskDetails(appState.params.id, appState.params.branch, taskNo)
const branches = getAllBranches(appState.params.id)
const frag = document.createDocumentFragment()
for (const branch of branches) {
const { branchName, parentBranch, startPoint, endPoint } = branch
if (parentBranch === appState.params.branch && startPoint === taskNo) {
frag.append(
render.branchButton({
projectId: appState.params.id,
branch: branchName,
page: 'project_explorer'
})
)
}
}
const assignedInterns = RIBC.getAssignedInterns(appState.params.id, appState.params.branch, taskNo).filter(v => v)
const assignedInternsCards = assignedInterns.map((internFloId) => render.assignedInternCard(internFloId));
const status = RIBC.getTaskStatus(appState.params.id, appState.params.branch, taskNo)
let applyButton
if (typeOfUser === 'intern' && !assignedInterns.includes(myFloID)) {
console.log(assignedInterns, myFloID)
const taskRequests = RIBC.getTaskRequests();
const hasApplied = [...taskRequests, ...sessionTaskRequests].find(({ projectCode, branch, task }) => {
return `${projectCode}_${branch}_${task}` === `${appState.params.id}_${appState.params.branch}_${taskNo}`
})
applyButton = html`
<button class="button apply-button" .dataset=${{ projectCode: appState.params.id, branch: appState.params.branch, taskNo }} ?disabled=${hasApplied}>
${hasApplied ? 'Applied' : 'Apply'}
</button>`;
}
return html`
<div class=${`task ${status}`}>
<div class="left">
<div class="circle"></div>
<div class="line"></div>
</div>
<div class="right">
<div class="apply-container flex space-between align-items-start">
<h4 class="timeline-task__title capitalize">${taskTitle}</h4>
${applyButton}
</div>
<div class="assigned-interns">${assignedInternsCards}</div>
<p class="timeline-task__description">${taskDescription}</p>
<div class="task__branch_container">${frag}</div>
</div>
</div>
`;
},
internCard(internName, internFloID, internPoints) {
const initials = internName.split(' ').map(v => v.charAt(0)).join('');
return html`
<li class="intern-card grid align-center interact" .dataset=${{ internFloId: internFloID }} title="Intern Information">
<div class="intern-card__initials" style=${`--color: var(${getInternColor(internFloID)})`}>${initials}</div>
<div class="intern-card__name capitalize">${internName}</div>
<div class="intern-card__score-wrapper flex align-center">
<h4 class="intern-card__score">${internPoints}</h4>
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <path fill="none" d="M0 0h24v24H0z" /> <path d="M12 18.26l-7.053 3.948 1.575-7.928L.587 8.792l8.027-.952L12 .5l3.386 7.34 8.027.952-5.935 5.488 1.575 7.928z" /> </svg>
</div>
</li>`;
},
internUpdateCard(update) {
let { floID, time, note, update: { projectId, projectBranch, task, topic, description, link } } = update
if (!topic) {
topic = `${RIBC.getProjectDetails(projectId).projectName} / ${RIBC.getTaskDetails(projectId, projectBranch, task).taskTitle}`
}
const internName = RIBC.getInternList()[floID]
let replyButton
if (typeOfUser === 'admin' && !note) {
replyButton = html`<button class="button button--small init-update-replay margin-left-auto">Reply</button>`
}
let providedLink
if (link) {
providedLink = html`<a href=${link} target="_blank" rel="noopener noreferrer">${link}</a> `
}
let adminReply
if (note) {
adminReply = html`<div class="admin-reply grid">
<h4 class="admin-reply__title">Admin</h4>
<p class="admin-reply__description">${note}</p>
</div>`
}
return html.node`
<li class="intern-update" data-vector-clock="${`${time}_${floID}`}">
<div class="flex align-center space-between">
<span class="update__sender">${internName}</span>
<span class="update__time">${getFormattedTime(time)}</span>
</div>
<h4 class="update__topic">${topic}</h4>
<p class="update__message">${description}</p>
${providedLink}
${replyButton}
${adminReply}
</li>`;
},
branchButton(obj = {}) {
const { projectId, branch, page, innerHTML } = obj
return html.node`
<a class="branch-button" href=${`#/${page}/project?id=${projectId}&branch=${branch}`}>
${innerHTML ? html`${innerHTML}` : branch}
</a>
`;
},
assignedInternCard(internFloId, options) {
let optionsButton
if (options) {
optionsButton = html` <button>
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></svg>
</button> `;
}
return html`
<span class="assigned-intern" data-flo-id="${internFloId}">
${RIBC.getInternList()[internFloId]}
${optionsButton}
</span>
`
},
taskListItem(taskNo) {
const assignedInterns = RIBC.getAssignedInterns(appState.params.id, appState.params.branch, taskNo)
const frag = document.createDocumentFragment()
const { taskTitle, taskDescription } = RIBC.getTaskDetails(appState.params.id, appState.params.branch, taskNo)
const status = RIBC.getTaskStatus(appState.params.id, appState.params.branch, taskNo)
let assignedInternsCards
if (assignedInterns) {
assignedInternsCards = assignedInterns.filter(v => v).map((internFloId) => {
return render.assignedInternCard(internFloId, true);
})
}
const branches = getAllBranches(appState.params.id)
for (const branch of branches) {
const { branchName, parentBranch, startPoint, endPoint } = branch
if (parentBranch === appState.params.branch && startPoint === taskNo) {
frag.append(
render.branchButton({
projectId: appState.params.id,
branch: branchName,
page: 'admin_page'
})
)
}
}
return html`
<li class="task-list-item" .dataset=${{ taskId: taskNo }}>
<div class="flex align-center gap-0-3">
<sm-checkbox ?checked=${status === 'completed'}>
<p class="margin-left-0-5">Mark as complete</p>
</sm-checkbox>
<button class="button--danger icon-only margin-left-auto"onclick="removeThisTask()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z"/></svg>
</button>
<button class="icon-only task-option">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <path fill="none" d="M0 0h24v24H0z" /> <path d="M12 3c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 14c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-7c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" /> </svg>
</button>
</div>
<h4 class="task-title capitalize" data-editable>${taskTitle}</h4>
<div class="assigned-interns">
<button class="button--outlined button--small button--colored" onclick="currentTask=this.closest('.task-list-item');openPopup('intern_list_popup')">
<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M11 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0-6c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zM5 18c.2-.63 2.57-1.68 4.96-1.94l2.04-2c-.39-.04-.68-.06-1-.06-2.67 0-8 1.34-8 4v2h9l-2-2H5zm15.6-5.5l-5.13 5.17-2.07-2.08L12 17l3.47 3.5L22 13.91z"/></svg>
Assign intern
</button>
${assignedInternsCards}
</div>
<p class="task-description" data-editable>${taskDescription}</p>
<div class="task__branch_container">${frag}</div>
</li>
`;
},
taskRequestCard(details) {
const { projectCode, branch, task, floID, vectorClock } = details
const internName = RIBC.getInternList()[floID];
return html`
<li class="request-card" .dataset=${{ vectorClock, type: 'task' }}>
<p class="request-card__description">
<b class="capitalize">${internName}</b> applied for
<b class="capitalize">${RIBC.getTaskDetails(projectCode, branch, task).taskTitle}</b> from
<b class="capitalize">${branch}</b> of
<b class="capitalize">${RIBC.getProjectDetails(projectCode).projectName}</b>
</p>
<div class="flex gap-0-3 margin-left-auto">
<button class="button button--small reject-request">
<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></svg>
Reject
</button>
<button class="button button--small accept-request">
<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg>
Accept
</button>
</div>
</li>
`;
},
internshipRequest(details) {
const { comments, floID, name, vectorClock } = details
let providedLink
if (typeof comments !== 'string') {
providedLink = html`<a class="internship-request__link" href="${comments.link}" target="_blank">${comments.link}</a>`
}
return html`
<li class="request-card" .dataset=${{ vectorClock, type: 'internship' }}>
<h4>${name}</h4>
<p class="request-card__description">${typeof comments !== 'string' ? comments.brief : comments}</p>
${providedLink}
<div class="flex gap-0-3 margin-left-auto" style="margin-top: 0.5rem">
<button class="button button--small reject-request">
<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></svg>
Reject
</button>
<button class="button button--small accept-request">
<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg>
Accept
</button>
</div>
</li>
`;
},
internTaskCard(taskObj) {
const { uniqueId, projectId, projectBranch, task } = taskObj
const { taskTitle, taskDescription } = RIBC.getTaskDetails(projectId, projectBranch, task)
const projectName = RIBC.getProjectDetails(projectId).projectName
return html`
<li class="task-card" data-unique-id="${uniqueId}">
<span class="task__project-title">${projectName}</span>
<div>
<h4 class="task__title">${taskTitle}</h4>
<p class="task__description">${taskDescription}</p>
</div>
<button class="send-update-button button--small margin-left-auto">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M1.946 9.315c-.522-.174-.527-.455.01-.634l19.087-6.362c.529-.176.832.12.684.638l-5.454 19.086c-.15.529-.455.547-.679.045L12 14l6-8-8 6-8.054-2.685z"/></svg>
Post an update
</button>
</li>
`;
},
dashProject(projectId, ref) {
const { projectName } = RIBC.getProjectDetails(projectId)
const projectMap = RIBC.getProjectMap(projectId)
const projectTasks = []
RIBC.getProjectBranches(projectId).forEach(branch => {
projectMap[branch].slice(4).forEach((task) => {
projectTasks.push(RIBC.getTaskStatus(projectId, branch, task))
})
})
const completedTasks = projectTasks.filter(task => task === 'completed').length
let completePercent = parseFloat(((completedTasks / projectTasks.length) * 100).toFixed(2))
const isPinned = pinnedProjects.includes(projectId);
let pinIcon = ''
if (isPinned) {
pinIcon = html`<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M8 6.2V4H7V2H17V4H16V12L18 14V16H17.8L14 12.2V4H10V8.2L8 6.2ZM20 20.7L18.7 22L12.8 16.1V22H11.2V16H6V14L8 12V11.3L2 5.3L3.3 4L20 20.7ZM8.8 14H10.6L9.7 13.1L8.8 14Z"/> </svg>`;
} else {
pinIcon = html`<svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M16 12V4H17V2H7V4H8V12L6 14V16H11.2V22H12.8V16H18V14L16 12ZM8.8 14L10 12.8V4H14V12.8L15.2 14H8.8Z"/> </svg>`;
}
return html.for(ref, projectId)`
<div class="pinned-card" data-id=${projectId}>
<div class="project-icon">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <path fill="none" d="M0 0h24v24H0z" /> <path d="M12.414 5H21a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7.414l2 2zM4 7v12h16V7H4z" /> </svg>
</div>
<div class="flex space-between align-items-start">
<a class="project__title" href=${`#/project_explorer/project?id=${projectId}&branch=mainLine`}>${projectName}</a>
<button class="icon-only pin-project" title=${`${isPinned ? 'Unpin' : 'Pin'} this project`} onclick="pinProject(this)" data-pinned=${isPinned}>${pinIcon}</button>
</div>
<div class="progress-bar">
<div class="progress-value" style=${`width: ${completePercent}%`}></div>
</div>
<span class="project__complete-percent">${completePercent}% complete</span>
</div>
`
},
dashProjects(where, projects) {
renderElem(where, html`${projects.map(project => render.dashProject(project, where))} `)
},
internRequests() {
const selectedRequestType = getRef('request_type_selector').value;
let requestCards = [];
if (selectedRequestType === 'task') {
RIBC.getTaskRequests().reverse().forEach((request) => {
const { projectCode, branch, task } = request
if (typeof RIBC.getTaskDetails(projectCode, branch, task) !== 'undefined')
requestCards.push(render.taskRequestCard(request))
})
} else {
requestCards = RIBC.getInternRequests().reverse().map((request) => render.internshipRequest(request))
}
renderElem(getRef('requests_list'), html`${requestCards}`)
},
projectList(container, projects, isAdminList = false) {
renderElem(container, html`${projects.map(projectCode => render.projectCard(projectCode, isAdminList, container))}`)
},
requestStatus(request, type) {
const { comments, floID, projectCode, branch, task, name, status, vectorClock } = request
const timestamp = parseInt(vectorClock.split('_')[0])
let details
let icon = ''
if (status === 'Accepted') {
icon = html`<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>`
} else if (status === 'Rejected') {
icon = html`<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69 16.9C4.63 15.55 4 13.85 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1C19.37 8.45 20 10.15 20 12c0 4.42-3.58 8-8 8z"/></svg>`
} else {
icon = html`<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></svg>`
}
if (type === 'internship') {
details = html` <h4 class="status-card__details">Internship application</h4> `;
} else {
details = html`
<p class="status-card__details">
You applied for
<b class="capitalize">${RIBC.getTaskDetails(projectCode, branch, task).taskTitle}</b> from
<b class="capitalize">${branch}</b> of
<b class="capitalize">${RIBC.getProjectDetails(projectCode).projectName}</b>
</p>`;
}
return html`
<li class=${`status-card ${status?.toLowerCase() || 'pending'}`}>
<time class="status-card__time">${getFormattedTime(timestamp, 'relative')}</time>
${details}
<div class="flex align-center status-card__status">
${icon}
<span>${status || 'Under review'}</span>
</div>
</li>
`
},
internApplications() {
const internshipRequests = RIBC.getInternRequests(false).reverse()
const taskRequests = RIBC.getTaskRequests(false).reverse()
const requestCards = internshipRequests.map(request => render.requestStatus(request, 'internship'))
renderElem(getRef('internship_requests_list'), html`${requestCards}`)
if (taskRequests.length) {
const taskCards = taskRequests.map(request => render.requestStatus(request, 'task'))
renderElem(getRef('task_requests_list'), html`${taskCards}`)
getRef('task_requests_list').parentElement.classList.remove('hidden')
} else {
getRef('task_requests_list').parentElement.classList.add('hidden')
}
}
}
const selectedColors = [
'--dark-red',
'--red',
'--kinda-pink',
'--purple',
'--shady-blue',
'--nice-blue',
'--maybe-cyan',
'--teal',
'--mint-green',
'--greenish-yellow',
'--yellowish-green',
'--dark-teal',
'--orange',
'--tangerine',
'--redish-orange',
]
function randomColor() {
return selectedColors[Math.floor(Math.random() * selectedColors.length)];
}
const renderedIntensColor = {}
function getInternColor(floId) {
if (!renderedIntensColor[floId]) {
renderedIntensColor[floId] = randomColor()
}
return renderedIntensColor[floId]
}
//helper functions
let allInternsList = [];
let highPerformingInterns = [];
let pinnedProjects = [];
let currentIntern;
let currentTaskId;
let typeOfUser = 'general';
// Adds interns to the database **Only SubAdmins can add interns
function addInternToList() {
let internName = getRef('intern_name_field').value.trim(),
internFloID = getRef('intern_flo_id_field').value.trim();
if (RIBC.admin.addIntern(internFloID, internName)) {
renderElem(getRef('admin_page__intern_list'), filterInterns(''))
closePopup();
notify(`${internName} added as an intern.`, 'success')
}
}
function addProjectToList() {
let projectName = getRef('project_name_field').value.trim(),
projectDescription = getRef('project_description_field').value.trim();
if (projectName === '') {
return notify('Project name is important!', 'error')
}
if (projectDescription === '') {
return notify('Project description is important!', 'error')
}
const projectCode = `${new Date().getFullYear()}_project_${RIBC.getProjectList() ? (RIBC.getProjectList().length + 1) : '1'}`;
RIBC.admin.createProject(projectCode)
RIBC.admin.addProjectDetails(projectCode, { projectName, projectDescription })
render.projectList(getRef('admin_page__project_list'), getSortedProjectList(), true)
getRef('admin_page__project_list').querySelector(`[href="#/admin_page/project?id=${projectCode}&branch=mainLine"]`)?.click()
closePopup();
}
function makeEditable(elem) {
floGlobals.tempEditableContent = DOMPurify.sanitize(elem.innerText.trim())
elem.contentEditable = true
elem.focus()
document.execCommand('selectAll', false, null);
}
getRef('project_details_wrapper').addEventListener('dblclick', e => {
if (e.target.closest('[data-editable]') && !e.target.closest('[data-editable]').isContentEditable) {
makeEditable(e.target.closest('[data-editable]'))
}
})
getRef('project_details_wrapper').addEventListener('focusout', (e) => {
if (e.target.isContentEditable) {
e.target.contentEditable = false
if (e.target.innerText.trim() !== '' && floGlobals.tempEditableContent !== DOMPurify.sanitize(e.target.innerText.trim())) {
const newTitle = DOMPurify.sanitize(getRef('editing_panel__title').innerText.trim())
const newDescription = DOMPurify.sanitize(getRef('editing_panel__description').innerText.trim())
RIBC.admin.addProjectDetails(appState.params.id, { projectName: newTitle, projectDescription: newDescription })
notify('Changes saved locally, commit the changes to make them permanent', 'success')
render.projectList(getRef('admin_page__project_list'), getSortedProjectList(), true)
} else {
e.target.innerText = floGlobals.tempEditableContent
}
}
})
// opens a popup containing various intern information
function showInternInfo(internFloId) {
const internName = RIBC.getInternList()[internFloId]
getRef('intern_info__initials').textContent = internName.split(' ').map(v => v.charAt(0)).join('');
getRef('intern_info__initials').style.setProperty('--color', `var(${getInternColor(internFloId)})`)
getRef('intern_info__name').textContent = internName;
getRef('intern_info__flo_id').value = currentIntern = internFloId;
getRef('intern_info__score').textContent = RIBC.getInternRating(internFloId); // points earned by intern
if (RIBC.getInternRating(internFloId) === 1) {
getRef('reduce_score_button').disabled = true;
}
openPopup('intern_info_popup');
}
// opens a popup containing various project information
function showProjectInfo(projectId) {
const frag = document.createDocumentFragment();
const { projectName, projectDescription } = RIBC.getProjectDetails(projectId);
getRef('project_explorer__project_title').textContent = projectName; // project name
getRef('project_explorer__project_description').textContent = projectDescription;
getRef('project_explorer__project_updates').href = `#/updates_page?projectId=${projectId}&internId=all`;
getRef('explorer_branch_container').innerHTML = ``;
RIBC.getProjectBranches(projectId).forEach((branch) => {
frag.append(render.branchButton({ projectId, branch, page: 'project_explorer' }))
})
getRef('explorer_branch_container').append(frag);
}
let currentTask = '';
function renderAdminProjectView(projectId) {
const allProjects = getRef('admin_page__project_list').querySelectorAll('.project-card');
const branchList = document.querySelectorAll('.branch-button');
const frag = document.createDocumentFragment();
allProjects.forEach(project => project.classList.remove('project-card--active'))
const targetProject = Array.from(allProjects).find(project => project.getAttribute('href').includes(projectId))
if (targetProject)
targetProject.classList.add('project-card--active')
getRef('branch_container').innerHTML = '';
branchList.forEach((branch) => {
branch.classList.remove('active-branch')
})
const { projectName, projectDescription } = RIBC.getProjectDetails(projectId);
getRef('editing_panel__title').textContent = projectName;
getRef('editing_panel__description').textContent = projectDescription;
RIBC.getProjectBranches(appState.params.id).forEach((branch) => {
frag.append(render.branchButton({ projectId: appState.params.id, branch, page: 'admin_page' }))
})
getRef('branch_container').appendChild(frag)
}
function renderBranchTasks() {
const { id: projectId, branch } = appState.params
const taskListContainer = appState.openPage === 'admin_page' ? 'task_list' : 'explorer_task_list';
let branchTasks = RIBC.getProjectMap(appState.params.id)[appState.params.branch];
getRef(appState.openPage).querySelectorAll('.branch-button').forEach((branchButton) => {
branchButton.classList.remove('active-branch')
})
getRef(appState.openPage).querySelector(`.branch-button[href="#/${appState.openPage}/project?id=${projectId}&branch=${branch}"]`).classList.add('active-branch')
if (branchTasks[1] && !taskListContainer === 'task_list') {
getRef(taskListContainer).textContent = "No tasks added yet, Please explore other projects"
} else {
let tasks = []
if (branch !== 'mainLine') {
const { startPoint, parentBranch } = getAllBranches(projectId).find(({ branchName }) => branchName === branch)
tasks.push(html`<p class="margin-bottom-0-5">
Branched off from <a href=${`#/${appState.openPage}/project?id=${projectId}&branch=${parentBranch}`}> ${parentBranch} </a>
</p>`)
}
if (taskListContainer === 'task_list') {
branchTasks.slice(4).forEach((taskNo) => tasks.push(render.taskListItem(taskNo)))
} else {
branchTasks.slice(4).forEach((taskNo) => tasks.push(render.taskCard(taskNo)))
}
renderElem(getRef(taskListContainer), html`${tasks}`)
}
}
function getAllBranches(projectId) {
const projectMap = RIBC.getProjectMap(projectId)
const projectBranches = RIBC.getProjectBranches(projectId)
const branchPoints = []
projectBranches.forEach((branch, index) => {
if (index > 0) {
const [parentBranch, , startPoint, endPoint] = projectMap[branch]
branchPoints.push({
branchName: branch,
parentBranch,
startPoint,
endPoint
})
}
})
return branchPoints
}
let currentViewIndex = 0;
getRef('admin_view_selector').addEventListener('change', (e) => {
const newViewIndex = parseInt(e.target.value);
showChildElement(getRef('admin_views'), newViewIndex, { entry: newViewIndex > currentViewIndex ? slideInLeft : slideInRight, exit: newViewIndex > currentViewIndex ? slideOutLeft : slideOutRight });
currentViewIndex = parseInt(e.target.value);
})
function toggleEditing(target) {
if (target === 'title') {
makeEditable(currentTask.querySelector('.task-title'))
} else {
makeEditable(currentTask.querySelector('.task-description'))
}
}
delegate(getRef('task_list'), 'change', 'sm-checkbox', (e) => {
currentTask = e.target.closest('.task-list-item');
const taskStatus = e.target.checked ? 'completed' : 'incomplete'
RIBC.admin.putTaskStatus(taskStatus, appState.params.id, appState.params.branch, currentTask.dataset.taskId)
})
getRef('task_list').addEventListener('focusout', (e) => {
if (e.target.isContentEditable) {
e.target.contentEditable = false
if (e.target.innerText.trim() !== '' && floGlobals.tempEditableContent !== DOMPurify.sanitize(e.target.innerText.trim())) {
const { taskTitle, taskDescription } = RIBC.getTaskDetails(appState.params.id, appState.params.branch, currentTask.dataset.taskId)
const taskDetails = {}
if (e.target.closest('.task-description')) {
taskDetails['taskTitle'] = taskTitle
taskDetails['taskDescription'] = DOMPurify.sanitize(e.target.innerText.trim())
} else if (e.target.closest('.task-title')) {
taskDetails['taskTitle'] = DOMPurify.sanitize(e.target.innerText.trim())
taskDetails['taskDescription'] = taskDescription
}
RIBC.admin.editTaskDetails(taskDetails, appState.params.id, appState.params.branch, currentTask.dataset.taskId)
notify('Changes saved locally, commit the changes to make them permanent', 'success')
} else {
e.target.innerText = floGlobals.tempEditableContent
}
}
})
getRef('task_list').addEventListener('dblclick', (e) => {
if (e.target.closest('[data-editable]') && !e.target.closest('[data-editable]').isContentEditable) {
makeEditable(e.target.closest('[data-editable]'))
}
})
getRef('task_list').addEventListener('click', (e) => {
if (e.target.closest('.task-list-item')) {
currentTask = e.target.closest('.task-list-item');
}
if (e.target.closest('.task-option')) {
const optionButton = e.target.closest('.task-option')
getRef('task_context').setAttribute('style', `top: ${optionButton.offsetTop}px`)
getRef('task_context').classList.remove('hidden')
getRef('task_context').animate([
{
transform: 'scaleY(0.95) translateY(-0.5rem)',
opacity: '0'
},
{
transform: 'none',
opacity: '1'
},
], {
duration: 200,
easing: 'ease'
})
.onfinish = () => {
getRef('task_context').firstElementChild.focus()
const y = document.addEventListener("click", function (e) {
if (e.target.closest('#context_menu') || e.target.closest('.task-option')) return;
getRef('task_context').animate([
{
transform: 'none',
opacity: '1'
},
{
transform: 'scaleY(0.95) translateY(-0.5rem)',
opacity: '0'
},
], {
duration: 100,
easing: 'ease'
}).onfinish = () => {
getRef('task_context').classList.add('hidden')
document.removeEventListener('click', y);
}
});
}
}
else if (e.target.closest('.assigned-intern button')) {
getConfirmation('Do you want to unassign this intern from this task?', { confirmText: 'Unassign' }).then((result) => {
if (result) {
RIBC.admin.unassignInternFromTask(e.target.closest('.assigned-intern').dataset.floId, appState.params.id, appState.params.branch, currentTask.dataset.taskId)
notify('Intern removed from the task')
renderBranchTasks()
}
})
}
else if (e.target.closest('.cancel-task-button')) {
const card = e.target.closest('.temp-task')
card.remove();
getRef('add_task').classList.remove('hidden')
}
else if (e.target.closest('.add-task-button')) {
const card = e.target.closest('.temp-task')
const titleContent = card.querySelector('.placeholder-task__title').value.trim(),
descriptionContent = card.querySelector('.placeholder-task__description').value.trim();
if (titleContent === '') {
notify('Please enter task title', 'error')
return
}
if (descriptionContent === '') {
notify('Please enter description of the task', 'error')
return
}
const taskNo = RIBC.admin.addTaskInMap(appState.params.id, appState.params.branch)
RIBC.admin.editTaskDetails({ taskTitle: titleContent, taskDescription: descriptionContent }, appState.params.id, appState.params.branch, taskNo)
RIBC.admin.putTaskStatus('incomplete', appState.params.id, appState.params.branch, taskNo)
card.remove()
renderBranchTasks()
getRef('add_task').classList.remove('hidden')
notify('Task added to current branch', 'success')
}
})
function addPlaceholderTask() {
const placeholderTask = getRef('placeholder_task_card_template').content.cloneNode(true)
getRef('task_list').append(placeholderTask)
getRef('task_list').lastElementChild.scrollIntoView({ behavior: 'smooth' })
getRef('task_list').querySelector('.placeholder-task__title').focusIn()
getRef('add_task').classList.add('hidden')
}
function commitToChanges() {
getConfirmation("Do you want to commit to changes?").then((result) => {
if (result) {
RIBC.admin.updateObjects().then(res => {
notify('Changes committed.', 'success')
}).catch(err => {
console.error(err)
})
}
})
}
function removeThisTask() {
getConfirmation("Are you sure to delete this task?", { confirmText: 'Delete' }).then((result) => {
if (result) {
RIBC.admin.deleteTaskInMap(appState.params.id, appState.params.branch, currentTask.dataset.taskId)
renderBranchTasks()
}
})
}
delegate(getRef('intern_list_container'), 'click', '.intern-card', (e) => {
const floId = e.target.closest('.intern-card').dataset.internFloId;
const internName = RIBC.getInternList()[floId]
const { projectName } = RIBC.getProjectDetails(appState.params.id)
if (RIBC.admin.assignInternToTask(floId, appState.params.id, appState.params.branch, currentTask.dataset.taskId)) {
notify(`${internName} assigned to ${projectName}`, 'success')
renderBranchTasks()
} else {
notify('intern already assigned', 'error')
}
closePopup()
})
function renderAllInterns() {
renderElem(getRef('all_interns_list'), filterInterns('', { sortByRating: true }))
}
function changeScore(scoreUpdate) {
let score = parseInt(getRef('intern_info__score').textContent)
score += scoreUpdate;
getRef('intern_info__score').textContent = score
document.querySelectorAll(`[data-intern-flo-id="${currentIntern}"]`).forEach(internCard => {
internCard.querySelector('.intern-card__score').textContent = score
})
if (score > 0) {
getRef('reduce_score_button').disabled = false;
RIBC.admin.updateInternRating(currentIntern, scoreUpdate)
}
if (score === 1 && scoreUpdate === -1) {
getRef('reduce_score_button').disabled = true;
}
}
function showNewBranchPopup() {
openPopup('create_branch_popup')
const startPoint = parseInt(currentTask.dataset.taskId)
getRef('branch_start_point').value = startPoint;
}
getRef('create_branch_btn').onclick = () => {
const startPoint = parseInt(currentTask.dataset.taskId)
const userMergePoint = getRef('branch_merge_point').value.trim()
const mergePoint = (userMergePoint === '') ? startPoint : parseInt(userMergePoint)
const branchName = RIBC.admin.addBranch(appState.params.id, appState.params.branch, startPoint, mergePoint);
notify(`Branch added ${branchName}`, 'success')
currentTask.querySelector('.task__branch_container').append(render.branchButton({ projectId: appState.params.id, branch: branchName, page: 'admin_page' }))
closePopup()
}
function renderProjectSelectorOptions() {
const options = [html`<sm-option value="all" selected>All</sm-option>`];
RIBC.getProjectList().reverse().forEach(project => {
options.push(html`<sm-option value="${project}">${RIBC.getProjectDetails(project).projectName}</sm-option>`);
})
renderElem(getRef('updates_page__project_selector'), html`${options}`)
}
function renderInternSelectorOptions() {
const options = [html`<sm-option value="all" selected>All</sm-option>`];
const allInterns = Object.entries(RIBC.getInternList()).sort((a, b) => a[1].toLowerCase().localeCompare(b[1].toLowerCase()));
allInterns.forEach(intern => {
options.push(html`<sm-option value="${intern[0]}">${intern[1]}</sm-option>`);
})
renderElem(getRef('updates_page__intern_selector'), html`${options}`)
}
function getUpdatesByProject(projectId) {
const projectName = RIBC.getProjectDetails(projectId).projectName
const allUpdates = RIBC.getInternUpdates()
const filteredUpdates = allUpdates.filter(({ update: { topic, projectId } }) => {
if (topic) {
return topic.includes(projectName)
} else if (projectId) {
return projectId === projectId
}
})
return filteredUpdates
}
function getUpdatesByIntern(floId, allUpdates = RIBC.getInternUpdates()) {
return allUpdates.filter(update => update.floID === floId)
}
function getUpdatesByDate(date, allUpdates = RIBC.getInternUpdates()) {
const filteredUpdates = []
const dateStart = new Date(`${date} 00:00:00`).getTime()
const dateEnd = new Date(`${date} 23:59:59`).getTime()
let isFromDate = false
for (const update of allUpdates) {
if (update.time > dateStart && update.time < dateEnd) {
filteredUpdates.push(update)
isFromDate = true
} else if (isFromDate) break
}
return filteredUpdates
}
let updatesLazyLoader
function renderInternUpdates(updates = RIBC.getInternUpdates()) {
if (updatesLazyLoader) {
updatesLazyLoader.update(updates)
} else {
updatesLazyLoader = new LazyLoader('#all_updates_list', updates, render.internUpdateCard)
}
updatesLazyLoader.init()
}
delegate(getRef('all_updates_list'), 'click', '.init-update-replay', (e) => {
const vectorClock = e.delegateTarget.closest('.intern-update').dataset.vectorClock;
e.delegateTarget.after(html.node`
<sm-form class="update-replay grid gap-0-5">
<sm-textarea placeholder="Enter your reply here" class="update-reply-textarea" rows="4" required></sm-textarea>
<div class="flex align-center gap-0-3 margin-left-auto">
<button class="update-replay__cancel button button--small" onclick="cancelUpdateReply(this.closest('.update-replay'))">Cancel</button>
<div class="multi-state-button">
<button class="update-replay__submit button button--small button--primary" onclick="submitUpdateReply(this.closest('.update-replay'))" type="submit">Submit</button>
</div>
</div>
</sm-form>
`)
e.delegateTarget.classList.add('hidden')
e.target.closest('.intern-update').querySelector('.update-reply-textarea').focusIn()
})
function cancelUpdateReply(replayBox) {
replayBox.previousElementSibling.classList.remove('hidden')
replayBox.remove()
}
function submitUpdateReply(replayBox) {
buttonLoader(replayBox.querySelector('.update-replay__submit'), true)
const vectorClock = replayBox.previousElementSibling.closest('.intern-update').dataset.vectorClock;
const replyText = replayBox.querySelector('.update-reply-textarea').value.trim()
if (replyText !== '') {
RIBC.admin.commentInternUpdate(vectorClock, replyText).then(res => {
replayBox.previousElementSibling.remove()
replayBox.replaceWith(html.node`
<div class="admin-reply grid">
<h4 class="admin-reply__title">Admin</h4>
<p class="admin-reply__description">${replyText}</p>
</div>`)
}).catch(err => {
notify(err, 'error')
buttonLoader(replayBox.querySelector('.update-replay__submit'), false)
})
}
}
function setUpdateFilters(filters) {
const { projectId, internId, date } = filters || getUpdateFilters()
if (filters) {
getRef('updates_page__project_selector').value = projectId
getRef('updates_page__intern_selector').value = internId
getRef('updates_page__date_selector').value = date || ''
} else {
const dateParam = date !== '' ? `&date=${date}` : ''
location.hash = `/updates_page?projectId=${projectId}&internId=${internId}${dateParam}`
}
}
function getUpdateFilters() {
const projectId = getRef('updates_page__project_selector').value || 'all'
const internId = getRef('updates_page__intern_selector').value || 'all'
const date = getRef('updates_page__date_selector').value || ''
return { projectId, internId, date }
}
function clearFilter() {
getRef('updates_page__project_selector').reset()
getRef('updates_page__intern_selector').reset()
getRef('updates_page__date_selector').value = ''
setUpdateFilters()
}
getRef('updates_page__project_selector').addEventListener('change', e => setUpdateFilters())
getRef('updates_page__intern_selector').addEventListener('change', e => setUpdateFilters())
getRef('updates_page__date_selector').addEventListener('change', e => setUpdateFilters())
function pinProject(thisBtn) {
const projectId = thisBtn.closest('.pinned-card').dataset.id;
pinnedProjects = localStorage.getItem(`${myFloID}_pinned_projects`) ? localStorage.getItem(`${myFloID}_pinned_projects`).split(',') : []
if (pinnedProjects.includes(projectId)) {
pinnedProjects = pinnedProjects.filter(project => project !== projectId)
} else {
pinnedProjects.push(projectId)
}
localStorage.setItem(`${myFloID}_pinned_projects`, pinnedProjects.join())
render.dashProjects(getRef('pinned_projects'), pinnedProjects)
render.dashProjects(getRef('project_list'), RIBC.getProjectList().filter(project => !pinnedProjects.includes(project)).reverse())
}
let sessionTaskRequests = new Set();
function requestForTask(btn) {
getConfirmation('Do you want to apply for this task?', { confirmText: 'Apply' }).then((result) => {
if (result) {
btn.textContent = 'Applied'
btn.disabled = true
const { projectCode, branch, taskNo } = btn.dataset
RIBC.applyForTask(projectCode, branch, taskNo).then((result) => {
notify('Applied successfully.', 'success')
sessionTaskRequests.add({ projectCode, branch, task: taskNo })
})
}
}).catch((error) => {
notify(error, 'error')
})
}
function toggleFilter() {
getRef('update_filters_wrapper').classList.toggle('hide-on-mobile')
}
// Event listeners
document.getElementById('top_interns').addEventListener('click', (event) => {
if (event.target.closest('.intern-card'))
showInternInfo(event.target.closest('.intern-card').dataset.internFloId)
})
document.getElementById('all_interns_page').addEventListener('click', (event) => {
if (event.target.closest('.intern-card'))
showInternInfo(event.target.closest('.intern-card').dataset.internFloId)
})
document.getElementById('admin_page__intern_list').addEventListener('click', (event) => {
if (event.target.closest('.intern-card'))
showInternInfo(event.target.closest('.intern-card').dataset.internFloId)
})
document.addEventListener('popupopened', e => {
switch (e.detail.popup.id) {
case 'intern_list_popup':
renderElem(getRef('intern_list_container'), filterInterns('', { availableInternsOnly: true }))
break;
}
})
document.addEventListener('popupclosed', e => {
switch (e.detail.popup.id) {
case 'intern_list_popup':
renderElem(getRef('intern_list_container'), html``)
getRef('intern_search_field').value = ''
break;
}
})
floGlobals.assignedTasks = {}
function renderAllElements() {
let sortedProjectList = getSortedProjectList()
document.querySelectorAll('.open-first-project').forEach(link => {
link.href = `${link.href}/project?id=${sortedProjectList[0]}&branch=mainLine`
})
pinnedProjects = localStorage.getItem(`${myFloID}_pinned_projects`) ? localStorage.getItem(`${myFloID}_pinned_projects`).split(',') : []
//creates cards for highest performing interns
//sort interns earned points
allInternsList = RIBC.getInternList();
highPerformingInterns = [];
for (let intern in allInternsList) {
highPerformingInterns.push({
floId: intern,
internName: allInternsList[intern],
rating: RIBC.getInternRating(intern)
})
}
highPerformingInterns.sort((a, b) => b.rating - a.rating);
// Intern's view
let assignedProjectsList = [];
if (allInternsList[myFloID] && !floGlobals.subAdmins.includes(myFloID)) {
typeOfUser = 'intern';
document.querySelectorAll('.intern-option').forEach((option) => {
option.classList.remove('hidden')
})
document.getElementById('pinned_project_section').classList.add('hidden')
// store all the projects assigned to interns in array
sortedProjectList.forEach(projectId => {
nextProject:
for (const branch in RIBC.getProjectMap(projectId)) {
const projectTaskNos = RIBC.getProjectMap(projectId)[branch].slice(4);
for (taskNo of projectTaskNos) {
const assignedInterns = RIBC.getAssignedInterns(projectId, branch, taskNo)
if (Array.isArray(assignedInterns) && assignedInterns.includes(myFloID)) {
assignedProjectsList.push(projectId)
break nextProject;
}
}
}
})
// store all the tasks assigned to intern in array
assignedProjectsList.forEach(project => {
for (const branch in RIBC.getProjectMap(project)) {
branchTasks = RIBC.getProjectMap(project)[branch].slice(4);
branchTasks.forEach(task => {
if (RIBC.getTaskStatus(project, branch, task) === 'incomplete') {
floGlobals.assignedTasks[floCrypto.randString(16, true)] = {
projectId: project,
projectBranch: branch,
task
}
}
})
}
})
let parent = document.getElementById('assigned_task_list');
if (!Object.keys(floGlobals.assignedTasks).length) {
parent.textContent = 'No task assigned yet.';
} else {
// Render assigned task cards
const taskCards = []
for (const task in floGlobals.assignedTasks) {
taskCards.push(render.internTaskCard({
'uniqueId': task,
...floGlobals.assignedTasks[task]
}))
}
renderElem(getRef('assigned_task_list'), html`${taskCards}`)
}
// Event delegate clicks on intern tasks
getRef('assigned_task_list').addEventListener('click', e => {
if (e.target.closest('.send-update-button')) {
const taskCard = e.target.closest('.task-card')
currentTaskId = taskCard.dataset.uniqueId
const { projectId, projectBranch, task } = floGlobals.assignedTasks[currentTaskId]
getRef('update_of_project').textContent = RIBC.getProjectDetails(projectId).projectName
getRef('update_of_task').textContent = RIBC.getTaskDetails(projectId, projectBranch, task).taskTitle
openPopup('post_update_popup')
}
})
}
else {
document.querySelectorAll('.intern-option').forEach((option) => {
option.classList.add('hidden')
})
document.getElementById('pinned_project_section').classList.remove('hidden')
}
// admin view
if (floGlobals.subAdmins.includes(myFloID)) {
typeOfUser = 'admin'
function removeRequest(requestCard) {
requestCard.animate([
{
transform: 'translateX(0)',
opacity: 1
},
{
transform: 'translateX(-100%)',
opacity: 0
},
], {
duration: 300,
easing: 'ease'
}).onfinish = () => {
requestCard.remove()
}
}
render.internRequests()
// accept task request
delegate(getRef('requests_list'), 'click', '.accept-request', (e) => {
getConfirmation('Are you sure you want to accept this request?').then(result => {
if (result) {
const vectorClock = e.delegateTarget.closest('.request-card').dataset.vectorClock
const type = e.delegateTarget.closest('.request-card').dataset.type
let result
if (type === 'task') {
result = RIBC.admin.processTaskRequest(vectorClock, true)
if (result === 'Accepted') {
notify('Intern assigned, commit changes to make it permanent.', 'success')
removeRequest(e.delegateTarget.closest('.request-card'))
}
} else if (type === 'internship') {
result = RIBC.admin.processInternRequest(vectorClock, true)
if (result === 'Accepted') {
notify('Added intern, commit changes to make it permanent.', 'success')
removeRequest(e.delegateTarget.closest('.request-card'))
}
}
}
})
})
// reject task request
delegate(getRef('requests_list'), 'click', '.reject-request', (e) => {
getConfirmation('Are you sure you want to reject this request?').then((result) => {
if (result) {
const vectorClock = e.delegateTarget.closest('.request-card').dataset.vectorClock
const type = e.delegateTarget.closest('.request-card').dataset.type
let result
if (type === 'task') {
result = RIBC.admin.processTaskRequest(vectorClock, false)
if (result === 'Rejected') {
notify('Request rejected', 'success')
removeRequest(e.delegateTarget.closest('.request-card'))
}
} else if (type === 'internship') {
result = RIBC.admin.processInternRequest(vectorClock, false)
if (result === 'Rejected') {
notify('Request rejected', 'success')
removeRequest(e.delegateTarget.closest('.request-card'))
}
}
}
})
})
document.querySelectorAll('.admin-option').forEach((option) => {
option.classList.remove('hidden')
})
//show interns
renderElem(getRef('admin_page__intern_list'), filterInterns(''))
//show projects
render.projectList(getRef('admin_page__project_list'), getSortedProjectList(), true)
} else {
document.querySelectorAll('.admin-option').forEach((option) => {
option.classList.add('hidden')
})
}
// General only view for non admin and non intern
if (!allInternsList[myFloID] && !floGlobals.subAdmins.includes(myFloID)) {
document.querySelectorAll('.general-only').forEach((elem) => {
elem.classList.remove('hidden')
})
}
else {
document.querySelectorAll('.general-only').forEach((elem) => {
elem.classList.add('hidden')
})
}
if (typeOfUser === 'admin') {
document.querySelectorAll('.not-for-admin').forEach((elem) => {
elem.classList.add('hidden')
})
} else {
document.querySelectorAll('.not-for-admin').forEach((elem) => {
elem.classList.remove('hidden')
})
}
if (typeOfUser === 'general') {
const internshipRequests = RIBC.getInternRequests(false).reverse()
const hasPendingApplication = internshipRequests.find(request => !request.status)
if (internshipRequests.length === 0 || !hasPendingApplication) {
getRef('application_card').classList.remove('hidden')
} else {
getRef('application_card').classList.add('hidden')
}
}
renderElem(getRef('top_interns'), html`${highPerformingInterns.slice(0, 4).map((intern) => {
const { internName, floId, rating } = intern
return render.internCard(internName, floId, rating)
})}`);
// displays recent projects
render.dashProjects(getRef('project_list'), RIBC.getProjectList().filter(project => !pinnedProjects.includes(project)).reverse())
if (typeOfUser === 'intern') {
render.projectList(getRef('my_projects'), assignedProjectsList)
sortedProjectList = sortedProjectList.filter(val => !assignedProjectsList.includes(val));
}
if (sortedProjectList.length > 0) {
getRef('other_projects').previousElementSibling.classList.remove('hidden')
render.projectList(getRef('other_projects'), sortedProjectList)
} else {
getRef('other_projects').previousElementSibling.classList.add('hidden')
}
getRef('explorer_task_list').addEventListener('click', (event) => {
if (event.target.closest('.apply-button')) {
requestForTask(event.target.closest('.apply-button'))
}
})
getRef('user_flo_id').value = myFloID;
console.log(typeOfUser)
}
getRef('request_type_selector').addEventListener('change', render.internRequests)
function postUpdate() {
const { projectId, projectBranch, task } = floGlobals.assignedTasks[currentTaskId]
const description = getRef('update__brief').value.trim()
const linkText = getRef('update__link').value.trim()
const link = linkText !== '' ? linkText : null
console.log(link)
if (description !== '') {
RIBC.postInternUpdate({ projectId, projectBranch, task, description, link })
.then((result) => {
notify('Update posted', 'success')
closePopup()
})
.catch((error) => {
notify(error, 'error')
})
}
else {
notify('Please enter description', 'error')
}
}
function filterInterns(searchKey, options = {}) {
const {
sortByRating = false,
availableInternsOnly = false
} = options
let filtered = [];
const allInterns = RIBC.getInternList()
let arrayOfInterns = []
for (const intern in allInterns) {
arrayOfInterns.push({
floId: intern,
internName: allInternsList[intern]
})
}
arrayOfInterns.sort((a, b) => a.internName.toLowerCase().localeCompare(b.internName.toLowerCase()))
if (availableInternsOnly) {
arrayOfInterns = arrayOfInterns.filter(intern => !RIBC.getAssignedInterns(appState.params.id, appState.params.branch, currentTask.dataset.taskId).includes(intern.floId))
}
if (searchKey === '') {
filtered = (sortByRating ? highPerformingInterns : arrayOfInterns).map(({ internName, floId }) => {
return render.internCard(internName, floId, RIBC.getInternRating(floId))
})
} else {
const options = {
keys: ['internName'],
threshold: 0.2
}
const fuse = new Fuse(arrayOfInterns, options)
filtered = fuse.search(searchKey).map(v => v.item).map(({ internName, floId }) => {
return render.internCard(internName, floId, RIBC.getInternRating(floId))
})
}
return html`${filtered}`
}
const searchInternPopup = debounce((e) => {
renderElem(getRef('intern_list_container'), filterInterns(e.target.value.trim(), { availableInternsOnly: true }))
}, 150)
const searchInternPage = debounce((e) => {
renderElem(getRef('all_interns_list'), filterInterns(e.target.value.trim(), { sortByRating: true }))
}, 150)
getRef('intern_search_field').addEventListener('input', searchInternPopup)
getRef('interns_page__search').addEventListener('input', searchInternPage)
function applyForInternship() {
buttonLoader(getRef('intern_apply__button'), true)
const name = getRef('intern_apply__name').value.trim();
const brief = getRef('intern_apply__brief').value.trim();
const link = getRef('intern_apply__link').value.trim();
const details = {
brief,
link
}
RIBC.applyForIntern(name, details)
.then((result) => {
notify('Application submitted', 'success')
getRef('application_card').classList.add('hidden')
closePopup()
})
.catch((error) => {
notify(error, 'error')
}).finally(() => {
buttonLoader(getRef('intern_apply__button'), false)
})
}
function getSortedProjectList() {
return RIBC.getProjectList().sort((a, b) => RIBC.getProjectDetails(a).projectName.toLowerCase().localeCompare(RIBC.getProjectDetails(b).projectName.toLowerCase()))
}
function getSignedIn(passwordType) {
return new Promise((resolve, reject) => {
routeTo('landing')
try {
getPromptInput('Enter password', '', {
isPassword: true,
}).then(password => {
if (password) {
resolve(password)
}
})
} catch (err) {
if (passwordType === 'PIN/Password') {
floGlobals.isPrivKeySecured = true;
getRef('private_key_field').removeAttribute('data-private-key');
getRef('private_key_field').setAttribute('placeholder', 'Password');
getRef('private_key_field').customValidation = null
getRef('secure_pwd_button').closest('.card').classList.add('hidden');
} else {
floGlobals.isPrivKeySecured = false;
getRef('private_key_field').dataset.privateKey = ''
getRef('private_key_field').setAttribute('placeholder', 'FLO private key');
getRef('private_key_field').customValidation = floCrypto.getPubKeyHex;
getRef('secure_pwd_button').closest('.card').classList.remove('hidden');
}
if (window.location.hash.includes('sign_in') || window.location.hash.includes('sign_up')) {
routeTo(window.location.hash);
} else {
location.hash = floGlobals.isPrivKeySecured ? '#/sign_in' : `#/landing`;
}
getRef('sign_in_button').onclick = () => {
resolve(getRef('private_key_field').value.trim());
getRef('private_key_field').value = '';
routeTo('loading');
};
getRef('sign_up_button').onclick = () => {
resolve(getRef('generated_private_key').value);
getRef('generated_private_key').value = '';
routeTo('loading');
};
}
});
}
function setSecurePassword() {
if (!floGlobals.isPrivKeySecured) {
const password = getRef('secure_pwd_input').value.trim();
floDapps.securePrivKey(password).then(() => {
floGlobals.isPrivKeySecured = true;
notify('Password set successfully', 'success');
getRef('secure_pwd_button').closest('.card').classList.add('hidden');
closePopup();
}).catch(err => {
notify(err, 'error');
})
}
}
function signOut() {
getConfirmation('Sign out?', { message: 'You are about to sign out of the app, continue?', confirmText: 'Leave', cancelText: 'Stay' })
.then(async (res) => {
if (res) {
await floDapps.clearCredentials();
location.reload();
}
});
}
async function downloadCredentials() {
try {
if (floDapps.user.id) {
const privKey = await floDapps.user.private;
generateTxtFile(`FLO address: ${floDapps.user.id}\n\nFLO Private Key: ${privKey} `, 'FLO credentials');
}
} catch (err) {
notify(err, 'error');
}
}
function downloadGeneratedCredentials() {
const floAddress = getRef('generated_flo_address').value;
const privKey = getRef('generated_private_key').value;
generateTxtFile(`FLO address: ${floAddress}\n\nFLO Private Key: ${privKey} `, 'FLO credentials');
}
// generate txt file with given content
function generateTxtFile(content, fileName) {
const element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content));
element.setAttribute('download', `${fileName}.txt`);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
</script>
</body>
</html>