\n );\n};\n","export default __webpack_public_path__ + \"static/media/Coffee.e30cbb95.png\";","export default __webpack_public_path__ + \"static/media/DarthVader.7145d163.png\";","export default __webpack_public_path__ + \"static/media/Inside.69f7e15c.png\";","export default __webpack_public_path__ + \"static/media/Panda.e39a1ef5.png\";","export default __webpack_public_path__ + \"static/media/Penguin.4c50261e.png\";","export default __webpack_public_path__ + \"static/media/Rabbit.730e9f3b.png\";","import React, { useEffect } from 'react';\nimport { useDispatch } from 'react-redux'\nimport { resetTitle } from '../../actions/titleActions';\nimport Coffee from '../../img/404/Coffee.png';\nimport DarthVader from '../../img/404/DarthVader.png';\nimport Inside from '../../img/404/Inside.png';\nimport Panda from '../../img/404/Panda.png';\nimport Penguin from '../../img/404/Penguin.png';\nimport Rabbit from '../../img/404/Rabbit.png';\n\nexport default (): JSX.Element => {\n\n const images = [ Coffee, DarthVader, Inside, Panda, Penguin, Rabbit ];\n\n const image = images[Math.floor(Math.random() * images.length)];\n\n const styles = {\n maxHeight: '60vh',\n maxWidth: '60vw'\n };\n\n const dispatch = useDispatch();\n useEffect(() => {\n if (!dispatch) {\n return;\n }\n\n dispatch(resetTitle());\n }, [dispatch]);\n\n return (\n
\n
\n \n
\n
\n );\n};\n","export default __webpack_public_path__ + \"static/media/DingLing.91fb71c3.mp3\";","import React, { useState, useEffect } from 'react';\nimport { useSelector, useDispatch } from 'react-redux'\nimport { GlobalStore } from '../../store';\nimport { useParams } from \"react-router-dom\";\nimport { subscribe, unsubscribe } from '../../actions/queueActions';\nimport { setTitle } from '../../actions/titleActions';\nimport { openShowMotdModal } from '../../actions/modalActions';\nimport Queue from '../../models/Queue';\nimport User from '../../models/User';\nimport RequestStatus from '../../enums/RequestStatus';\nimport EnterQueueViewComponent from './EnterQueue';\nimport QueueAdministratorOptionsViewComponent from './QueueAdministratorOptions';\nimport QueueEntryTableViewComponent from './QueueEntryTable';\nimport PageNotFound from '../NoMatch';\nimport SearchViewComponent from '../../viewcomponents/Search';\nimport { Lock } from '../../viewcomponents/FontAwesome';\nimport DingLing from '../../sounds/DingLing.mp3';\n\nexport default (): JSX.Element | null => {\n\n const { queueName } = useParams();\n\n const user = useSelector(store => store.user);\n const queue = useSelector(store => store.queues.queueList.filter(q => q.name === queueName)[0] || null);\n const queuesAreLoaded = useSelector(store => store.queues.requestStatus === RequestStatus.Success);\n const playSounds = useSelector(store => store.playSounds);\n\n const [filter, setFilter] = useState('');\n const [previousQueueEntryCount, setPreviousQueueEntryCount] = useState(0);\n const [hasShownMotd, setHasShownMotd] = useState(false);\n\n const dispatch = useDispatch();\n\n useEffect(() => {\n if (queue !== null) {\n if (queuesAreLoaded && !hasShownMotd && queue.motd) {\n dispatch(openShowMotdModal(queue.motd));\n setHasShownMotd(true);\n }\n }\n }, [queuesAreLoaded, queue, dispatch]);\n\n useEffect(() => {\n if (queueName !== null && queueName !== undefined && queuesAreLoaded) {\n dispatch(subscribe(queueName));\n return () => {\n dispatch(unsubscribe(queueName));\n };\n }\n }, [queueName, queuesAreLoaded, dispatch]);\n\n useEffect(() => {\n if (queue !== null) {\n if (user !== null) {\n for (let i = 0; i < queue.queueEntries.length; i++) {\n if (queue.queueEntries[i].ugkthid === user.ugkthid) {\n dispatch(setTitle(`[${i+1}/${queue.queueEntries.length}] ${queue.name} | Stay A While`));\n return;\n }\n }\n }\n dispatch(setTitle(`[${queue.queueEntries.length}] ${queue.name} | Stay A While`));\n }\n }, [queue, user, dispatch]);\n\n useEffect(() => {\n if (!queue) {\n return;\n }\n\n if (!user?.isTeacherIn(queue.name) && !user?.isAssistantIn(queue.name)) {\n return;\n }\n\n if (!queue.queueEntries) {\n setPreviousQueueEntryCount(0);\n }\n else {\n const newEntryNumber = queue.queueEntries.length;\n if (previousQueueEntryCount === 0 && newEntryNumber === 1 && playSounds) {\n new Audio(DingLing).play();\n }\n setPreviousQueueEntryCount(newEntryNumber);\n }\n }, [queue?.queueEntries]);\n\n return (\n !queuesAreLoaded\n ? null\n : queue === null\n ? \n :
\n
\n {\n queue.locked\n ?
{queue.name}
\n :
{queue.name}
\n }\n
{queue.info}
\n
\n \n
\n
\n
\n
\n \n \n
\n
\n \n
\n
\n
\n );\n};\n","import React from 'react';\nimport { Link, useLocation } from \"react-router-dom\";\nimport { useSelector } from 'react-redux'\nimport { GlobalStore } from '../store';\nimport User from '../models/User';\nimport Logo from '../img/logo-stay-a-while.png';\n\nexport default (): JSX.Element => {\n\n const user = useSelector(store => store.user);\n\n function hideNavBar() {\n ($('#navbarText') as any).collapse('hide');\n }\n\n function toggleNavBar() {\n ($('#navbarText') as any).collapse('toggle');\n }\n\n const location = useLocation();\n\n return (\n \n );\n};\n","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXQAAABWCAYAAADfcG3aAAAACXBIWXMAAAsSAAALEgHS3X78AAAOzklEQVR4nO3dCVRVdR4H8FtNU53EzFMabuMSq4/liMqIuWX2conkOYJoOZoKLtM2qCk0TUczM6AxG/cFhxIkRjQxjeOQiskkiyEgirigiIQ5hoJH89S8Ob/xwUHeu/vy7rvv+znHc/S96//+7//+3+/e+9/ufVarlQEAANd3P84hAIAxIKADABgEAjoAgEEgoAMAGAQCOgCAQSCgAwAYBAI6AIBBIKADABgEAjoAgEEgoAMAGAQCOgCAQSCgAwAYBAI6AIBBIKADABgEAjoAgEEgoAMAGAQCOgCAQSCgAwAYxG9c4TBmJyyNnhEVkca1zeaMnZPXLftLuna5AnA/evstfrQ+JW7EoAFJQvMjdntX4xIBnQwINHF+vzljp2Z5AXBnevstis2PkWMJmlwAAAwCAR0AwCAQ0AEADAIBHQDAIBDQAQAMAgEdAMAgXGbYIgBAW6lZ2ZtTs7KLeQqmxF0KDgEdAFxWeU5WA8MwB3EG7+IN6CazpWfCvFmLuz7VafgjDz/krdSOb93++XTtD1cOLlu9cXl5Tla13PRo9lphaTnnDLZmJ6rOVUybEN63+d9CZo8p4c6dX+ouXq7L/urA4W2f/+2DPK4kt+7YfaKvV29/tu9z8wsyF81+NVJKtpw9W06P5S3XG0tWBIX1C9rfu0e3J9mSunqtoX7B8o99bUFIML66UFJRWThr0oSBYtKk3/XH7ywoe/wxj3Yc6f7nk5RtT4vNr5jfIptff/1v4/ma2vSyyqrsDxa8vodtO73M/KTyjJs59U9dOncK6dih/XCl0m2uw8tWb1xbnpPF/6RhtVpZ/6xYtyWu5vIPDVYVUfq0H658xMYviVYyB/868l1R6/Rt+9fMjcYm6+rU7e9xHbMtj6zSvty7nuv/851XvmO1lbmk9JXYv5KElLcSf2znhBNfXW/7Jz5xVYyQohB7vmzlwclRHVP6t8iHzh3lo+/zER2UqMtq1P2kjf+IPl9zqVTNcrje2PiTkDrM2imakrkraWHs9KRunp0fU+pq4wilT/uhK62a+9ETj3aPMnNfifpr+u59693lmJ1Jq/I+dLSI99E/yM9nnJg0B/ULelHIduNGDpssJt0+v+s+jOv7xqabDD3ZiElTDXTuosNHxyTMm7XC2XlxJHlTavSsSZY1Pbt1DVBzP+3btetAdZjiMtd2DgM6ZfIPo0dpGmDnTIlMik9cJaqyuzqqqO50IXM2tcubHutz849ydtCF9QsaLrSev/xW/NBhA0MEbUvb0fZC06V8cG2Tf+z4QbWbqcSgc7fms4z39JIfxtbMNnHMqA8p2Gq1T4rLFJ/ZvncY0IP9feLoyqgl2t/IwaG6OmFaGD38mTdNZotmFcLdqV3elWerWdt7GVs9D/DxEnTXPXbEkClCf4e0HW2vVLpnL9QcErRjDYU/N/xNCqJ6yc/vgwPn9uji2UPLfdJ5o/jM9r1dQKflMUeGhYaonjMHaL/jY94QVNmNIsDHq9tLo0ZI6twE8dQu7zWfZ2y9VFd/nWubIQP6RVEnGtc29P3g/sEviNk3pSsk4NEAB67vKf90HGL2rQVqnvXp1TNCD3mhm4IgP+9Jztg3xUmK046+swvogb7ePprkisXzQ8KccjFxJpP30253zM6kZnnTiK3DhccyuLahwDT35ahpXNvQiAmxd3+UbrCfzytc2yQkfRozdGAI52i1wtLyvUqMPFODZ6cnFBtpJwfdFPh79WnvrP2HBgc4rMN2Af2Rhx9iHcakBWfv3xkefui3Hu52zM6kdnkL6Rz16dOTtW2c7v4C/byfkbLv/oF9o7ialPoH+PMOqSsqq9DtuO4HH3xQF78Vq9Xq6cz9375zp5Ojz0VPLDpSVFIXm7BEVI96a+uXvZs2uH+w4oUx9tV571+orcsVuHlDeU5Wyz8EzjaTbOmf562MMI9kfRTWSyXVitHL29Y5GsfVdNn82OxofPRUy4szQkz+oVL2TU1KcTOnvsMwzPy231Ezztdb147h+v95BcWnl81/bYOUfTcT+Vu8x+I5MxZPGT/2eTn710KH9h5NfLsxmS1vSZ2lyleHO3h43HL0uaSZouU5WZKv4Cazhe9iUD194njR6Xb3fKriqy2rJeVL7dlmuflHf1ErbVfkDuVNnaN8fVHmoWHUX2QX0P29+siamEJ393SX3nZCEDXz8A1Dpsl+cvbNyPwt7vkmz0ijvkqkxkqpdVjzqf9yLgYAroI6FWlUBlcAHRBoGkOdmJ+8+/bx5s9oSOPiOTNkDd+lu3u6y6cRyK0/Fzr2PDp8NOqZixId0AN9vTwLS8utcg/3RtPN4itXrxUrNfVfDrWnovv26aXJcbgKdyhvqtM7c3L3dvPszDpmuFUnZktApyGNSgwZDgnwj24d0Gns+dqlCS419hzEswvot27/zNk2RJWN7yWrAtHjaAgNzUrelLoobuZUp75lW6FjApR3i5y8/OwI80jWgE76mfzGmMyW96l5hO7WF8yaFsWXbm39FaZrZ4d9Yi3attELGXt+/GTlHvPQML7dg47ZBfTSU6crtcwuDc2KmTQhbfMXO9vPiIzAVHgwDCGdo0F+Pn7NzSN0ty5kqY2EpE8ztyYunci3nW05gHRqT9+2cvkQrm3LKqsuUWf1wtjpqIAKSUlccqCwtFxSYlKfMu0CupBKqDS6c6AptPGJq2q5VlYDcDUXa+sO2p5GWVEnqMls2bxt5XLOESjM3c6y4q2JSyNz8vIPmIeGcTah0HIA1Cb/0qgRXejCwbVteeWZvWJXVQRuzngKdTj1v6SiMpk6SLRE6yGEhQTHanz8AKpK3pT6d76ZoxR4h4X2X8kXdJlWSwtQ8wjfts3LDAhZ4EvI2HnQP4cBndqz/7lvf7LWuRezwBCAK6DOUZp5yZVVCrzx82b+ke9wqFlkzecZK+nvC2OnJ/MtBEbGPjsksnf3rpy/KUpH7fXCQRusy+dOnzh+/kfrU+bz3V0oiSp2oK/XAJx7MBLqHOU7HL5OTlJUeiKjdbNIcVkFbxCmJ1++Keq2ZiEwAM5hi3QXYDJbdrR+Y5Fv716MmisxPtnxcadOqWUjZ/Yb36wvsGek8laiX4purEpOVn7W+jPqxKTVI2l2qNR0bzQ1NeTmF+yWMpkP9Id3HLptjHhL27bJbAmmmadSj0Stqf9qkzP7DTNFxTNaeQvpHOVCC361noDE2GbcpmTuygjw8ZI8uzKv4Ni3GHtuHKInFgl6rx2HI0Vu8wJugBZ0d32prn6mlDeA0QAFerdmNGM/g5M6XUcODp0odV3uiqqzB8c9i24rNchZy0WABkczeu0COr0No0unJ1mHQzU0Nj4yZ0rkVC0LBsDV0d0138xRNocKivewDeelJ+j03fu+7hHuGSM2XYw9V53ktVyksgvoHR9rH0Kve2JLj1Zb1ElhAbgUWpaWb+aoI/8+djyb6y76u5LSNfRyC7F3/xh7bjyar+US6OulSiHOiIpIKywtTxOy7YmqcxXTJoT3bf73uYuXLiuZfltqrC0yNDQkprC0XPRdGRkxyLkDiVyxvJVAy9Lm5h+NEdM5SkMK+Zazpbv/9N37MrhuxByhsefuthDX9camRr5t2OpeUVnFZ2JaJ+TMFOWTm1+QuWj2q3Zv3nLmWi4O/XjtJ7sngG+Lvj9J7Yhco2vE5OlG08171hL+4cervGsbaz3r69btnzmfhGiYm5Chbnqkx/LWitjOUdsyvLzb0SqJNOZc6AuL3XXs+YnTZ3hbGNjq3pnqmpa4IeTCoGYdPlNd85Ojz+3GoWu9lktrFLRLT1UVtv2cOmK/rzh1Wq397trwSTYt7K9W+lI0Nt3kvYt1VXosb63YOkcFze1oPZGID41UoRErQg+D72XWRnXmQs1hKle5h/fl/gNfVFSdvaG3YrIL6LYxs6q9TYYLdf6wDaE6e6GGd3KGHGqnLxbdcdEYYT3lSUl6K2+tUPMI38zRZm0nEvHJLy5ZL2TJDr2+BFoLVJ7UdyB3V5TO8ZOnt+vt+BzOFP0mv0DWK6ikoOBFFZLtvyZvSn3/+MnKk2rtn4Z/VV+qLVMrfbHowvbVN4e/0Et+lKa38taSkHd2OppIxIdGwtBNEd92en4JtBboHQwXL9ddlLsr6oxWIh0lOQzo1AmTkrlLs7Vc6K4ic+/+RVwrLdIV8dDR4tlqFSBV8A3pO+KvNzbppqIvW73x7SNFJTt0kBXF6bG8tUK/L74mJ0cTiYTYk3uItyNZzy+B1gLVvXXbMufJfQKm80NxS09P0k5fy4UC9IbtOyYLWQv99WmT85I3pYYXl1ccVSMvdEFZsW7La2o+CYhBF7HYhCUz03fv26D16pda0Ft5a4mryan5VXBSssPXZEplTe2/OisOzVHd+3Dtllfk1j1ayHDj9qy5eqnDmq/lQpX11Lnzkl9BR1dFk9nywtyXo97s0dUzpPMTHcd5PPqoYkPV6ESbzJZv6c3pXTp3CunYof3wLk4cUWJrQ42NT1yVTUuh9ureNfqBB+73MMooEL2Vt1a4Zo5y9SUJQYt2sQ2NrKg6dxhjz+9Squ5RUDeZLfv0UIfvs1rFDSmXu5YLTTaVu3xAW/RGFoZhgtXKg8ls6ckwTE/ZGVUoP8zdPEl+M/zAIFPklo+WzOHaZnbC0snOGtamx/JWA8c5rJbTxs3ze1Az7WYlUi8aAuLLPedO6fzw1D3BZeesOiw6oIPro5c0L4ydzvmSZmcGdACQhrUNHQAAXAsCOgCAQYheywWcZ9XWtMpB/QK95WZAyFouQqbnA4C+IKC7kL7efRq1GN1CU5ppirSBig7ALaDJBezU1NUfw9A2ANeDgA52LtdfccpaPgAgDwI63INm4dK6OSgVANeDgA4taBmG1KzsWDS3ALgmdIrC/9GdOQVzKQtCAYA+IKC7qVNnzzONN28y9Vev7blYW1dML1LAnTmAa8PUfxciZ/0WB2St6QEA+oOADgBgEOgUBQAwCAR0AACDQEAHADAIBHQAAINAQAcAMAgEdAAAg0BABwAwCAR0AACDQEAHADAIBHQAAINAQAcAMAgEdAAAI2AY5n/sfMdFDZdRkwAAAABJRU5ErkJggg==\"","export default class Contributor {\n\n private _name: string;\n public get name() { return this._name; }\n\n private _gravatar: string;\n public get gravatar() { return this._gravatar; }\n\n private _github: string;\n public get github() { return this._github; }\n\n private _linkedIn: string;\n public get linkedIn() { return this._linkedIn; }\n\n public constructor(data: any) {\n this._name = data.name || '';\n this._gravatar = data.gravatar || '';\n this._github = data.github || '';\n this._linkedIn = data.linkedIn || '';\n }\n\n public static InitialValue: Contributor[] = [\n new Contributor({\n \"name\": \"Anton Bäckström\",\n \"gravatar\": \"7eaf43cc9a0edf01b4994318e03fe368\",\n \"github\": \"antbac\",\n \"linkedIn\": \"428598244\"\n }),\n new Contributor({\n \"name\": \"Robert Welin-Berger\",\n \"gravatar\": \"5577c2569cfb1fe29388d1e2c1a794d7\",\n \"github\": \"avacore1337\",\n \"linkedIn\": \"332859123\"\n })\n ];\n\n public clone(): Contributor {\n return new Contributor({\n name: this._name,\n gravatar: this._gravatar,\n github: this._github,\n linkedIn: this._linkedIn\n });\n }\n}\n","import React from 'react';\n\nexport default (props: any): JSX.Element | null => {\n const contributor = props.contributor;\n\n return (\n contributor.github\n ? \n {contributor.github}\n \n \n : null\n );\n};\n","import React from 'react';\n\nexport default (props: any): JSX.Element | null => {\n const contributor = props.contributor;\n\n return (\n contributor.linkedIn\n ? \n {contributor.name}\n \n \n : null\n );\n};\n","import React from 'react';\nimport GitHubLink from './GitHubLink';\nimport LinkedInLink from './LinkedInLink';\n\nexport default (props: any): JSX.Element => {\n const contributor = props.contributor;\n\n return (\n
\n
\n \n
\n
\n
{contributor.name}
\n \n \n
\n
\n );\n};\n","import React, { useEffect } from 'react';\nimport { useDispatch } from 'react-redux'\nimport { resetTitle } from '../../actions/titleActions';\nimport Contributor from '../../models/Contributor';\nimport Contributors from '../../data/contributors.json';\nimport ContributorElement from './Contributor';\n\nexport default (): JSX.Element => {\n\n const contributors: Contributor[] = Contributors.Contributors.map(contributor => new Contributor(contributor));\n\n const dispatch = useDispatch();\n useEffect(() => {\n if (!dispatch) {\n return;\n }\n\n dispatch(resetTitle());\n }, [dispatch]);\n\n return (\n
\n
\n
About Stay A While
\n
\n Welcome to Stay A While, the queueing system of KTH\n
\n\n
\n Stay A While was originally developed as part of the DD1392 course back in 2015 and was a cooperation\n between students and faculty members.\n The purpose of the application was to provide a complete framework for students and teaching assistants to queue,\n and manage the queueing users during lab sessions.\n The system was designed with a sleek web interface, and was intergrated with the official KTH user-system,\n making it easy for students to use across different devices.\n As such, the system quickly replaced previous queueing systems and was adopted as the default queueing system\n of the now called EECS school and its related sister schools.\n
\n
\n Eventually, the system was adopted by other schools at KTH, at which point it was decided that the system\n would receive a rework to improve both performance, visuals, as well as security.\n Thus, Stay A While was developed in 2020 and was a complete rewrite of the original system.\n
\n );\n}\n","import React from 'react';\nimport { useSelector } from 'react-redux'\nimport { HashLink as Link } from 'react-router-hash-link';\nimport { GlobalStore } from '../../../store';\nimport User from '../../../models/User';\n\nexport default (): JSX.Element | null => {\n\n const user = useSelector(store => store.user);\n\n return (\n user?.isAdministrator\n ?
\n
Administrators
\n\n
\n Administrators are the supreme rulers of Stay A While, with privileges to perform just about anything.\n Their primary purpose is adding new queues, and adding teachers for those queues.\n
\n\n
Set server-message
\n
\n To add a server-message, go to the Administration page.\n Then click the Set server-message button and enter a message in the window that opens up.\n Once you have entered your message, click the Set message button to save your message.\n A server-message will then be shown to every user currently connected to Stay A While as\n well as those that connect while the message is active.\n A server-message may be useful to forewarn users upon updating the service.\n
\n
\n To remove a server-message, go to the Administration page.\n Then click the Set server-message button and then click the Remove message button\n on the window that opens up.\n
\n\n
Add an administrator
\n
\n To add an administrator, go to the Administration page.\n Then enter the username of the user you wish to add as administrator in the field which\n says Add administrator and click Add.\n If the entered username is correct, the user will immediately show up in the list of administrators\n below the input field.\n
\n
\n \n Note: New administrators will get their privileges once they login next time, so they may have to\n log out and back in again.\n \n
\n\n
Remove an administrator
\n
\n To remove an administrator, go to the Administration page.\n Then, under Administrators, click the red cross next to the name of the administrator you wish to remove.\n The user should now disappear from the administrator list.\n If not, please refresh the page.\n The administrator rights will be removed from the user immediately.\n
\n
\n \n Note: There must always be at least one administrator.\n It is therefore not possible to remove an administrator if it is the last one remaining.\n \n
\n\n
Add a queue
\n
\n To add a new queue, go to the Administration page.\n Then enter the name of the queue you wish to add in the field which says Add queue and click Add.\n The queue should now show up in the list of queues below the input field.\n
\n
\n To remove a queue, see Remove queue under the Teacher section.\n
\n\n
Add or remove teachers and assistants
\n
\n See \n Add a teacher or assistant\n and \n Remove a teacher or assistant\n under the Teacher section.\n
\n\n
Accessing statistics
\n
\n See Statistics under the Teacher section\n
\n
\n : null\n );\n};\n","export default __webpack_public_path__ + \"static/media/remove-teacher.685ef475.png\";","import React from 'react';\nimport { useSelector } from 'react-redux'\nimport { HashLink as Link } from 'react-router-hash-link';\nimport { GlobalStore } from '../../../store';\nimport User from '../../../models/User';\nimport EditQueue from '../../../img/edit-queue.png';\nimport RemoveTeacher from '../../../img/remove-teacher.png';\n\nexport default (): JSX.Element | null => {\n\n const user = useSelector(store => store.user);\n\n return (\n user?.isAdministrator || user?.isTeacher()\n ?
\n
Teacher
\n\n
\n Teachers are in charge of specific queues.\n In addition to the privileges of an assistant, they can also add and remove assistants as well\n as teachers from their queues\n and hide or remove the queue itself.\n
\n\n
Add a teacher or assistant
\n
\n To add a teacher or assistant for a queue, go to the Administration page.\n Then choose the queue you wish to add a teacher or assistant for in the dropdown as below:\n
\n
\n \n
\n
\n Once you have selected a queue, enter the name of the user you wish to add either in the\n Add teacher or the Add assistant input fields, and click the Add button.\n If done correctly, the user should now show up in the list under the given input field.\n
\n\n
Remove a teacher or assistant
\n
\n To remove a teacher or assistant from a queue, go to the Administration page.\n Choose the queue you wish to remove a teacher or assistant from in the dropdown, the way you would\n when adding a teacher.\n Click the red cross beside the user you\n want to remove, as in the picture below.\n
\n
\n \n
\n
\n Once the name in the list is gone, so are their privileges.\n
\n\n
Hide a queue
\n
\n Hiding a queue means both purging and hiding the queue.\n This can be used for example when there will be no labs in a queue for a longer time.\n
\n
\n To hide a queue, go to the Administration page.\n Then go into the Select queue dropdown as you would adding\n a teacher or assistant, and click the Hide queue button that shows up upon selecting a queue.\n Confirm in the popup that appears.\n
\n
\n When you want to show the queue again, follow the same procedure and click the Reveal queue button\n which is now shown instead of the Hide queue button.\n
\n\n
Removing a queue
\n
\n Removing a queue means removing it from the system.\n There is no reversing this action and all privileges will be removed along with the queue.\n
\n
\n To remove a queue, go to the Administration page.\n Then go into the Select queue dropdown as you would adding\n a teacher or assistant, and click the Remove queue button that shows up upon selecting a queue.\n Confirm in the popup that appears.\n
\n
\n \n Note: If you wish to regain a removed queue, you will have to contact an administrator.\n \n
\n\n
Statistics
\n
\n Stay A While allows the teacher to access some information about how a queue has been used.\n
\n
\n To access the statistics, first go to the Statistics page.\n Then go into the Select queue dropdown, and select the desired queue.\n Then enter a starting point and an end point.\n Once the desired time-period has been entered click, the Get statistics button and the information\n should be shown below.\n
\n
\n : null\n );\n};\n","export default \"data:image/png;base64,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\"","import React from 'react';\nimport { useSelector } from 'react-redux'\nimport { HashLink as Link } from 'react-router-hash-link';\nimport { GlobalStore } from '../../../store';\nimport User from '../../../models/User';\n\nexport default (): JSX.Element | null => {\n\n const user = useSelector(store => store.user);\n\n return (\n user?.isAdministrator || user?.isTeacher() || user?.isAssistant()\n ?
\n
Assistants
\n\n
\n Queue assistants help with moderating specific queues.\n In addition to removing specific users,\n they are also able to lock and purge queues, as well as sending messages and interact\n with the users in the queue.\n
\n
\n \n Note: All the assistants privileges are located in the queue.\n You get there by choosing the right queue from the list of queues.\n \n
\n\n
Kicking a user
\n
\n To remove a queue position, first double-click the user you wish to remove and thereafter click the appering red\n button with a cross on it.\n Be careful when removing users from the queue, as this action can not be reverted.\n
\n
\n \n Note: If you are accessing the service through a recognized smartphone device, you will only have to click once\n to show the option to kick a user.\n \n
\n\n
Message a user
\n
\n In order to send a message to a certain user in the queue, access the options as you would kicking a\n user, and click the blue button with an envelope on it.\n Enter a message in the window that opens up and click Send.\n
\n
\n To send a message to everyone in the queue, see broadcast.\n
\n\n
Help a user
\n
\n By helping a user, you mark them using the blue button with a checkmark accessed in the same way as you would access the\n button to kick someone.\n When a user is receiving help, their row will have a green background, notifing any other assistant that they can move to the next person\n in the queue.\n
\n\n
Bad location
\n
\n If an assistant is unable to find a given user because they have entered a location that is not precise enough, the\n assistant has the option to mark the user for Bad location.\n Marking a user for bad location is done by clicking the yellow button with a question-mark on it.\n The button is found at the same place as the one used to kick a user.\n
\n
\n The user will then receive a message prompting them to edit their location.\n The user will then have a red color until they have edited their location.\n
\n\n
Broadcast
\n
\n If you want every user in the queue to know something you can user the ability to broadcast information.\n
\n
\n Below the option to join the queue assistants will find a dropdown with administrative options.\n The first one being Broadcast.\n
\n
\n Broadcasting is done by clicking the dropdown with administrative options and clicking Broadcast, this will\n open a window prompting the user to enter a message to broadcast.\n Once the message is entered, click the Broadcast button to send it.\n
\n
\n \n Note: By using this function, everyone in the room will see the message.\n If you only want the assistants and teachers to see the message, use Broadcast faculty\n as described below.\n \n
\n\n
Broadcast faculty
\n
\n If you want every assistant and teacher in the given room you can user the ability to broadcast information to faculty.\n
\n
\n Broadcasting to faculty is done by clicking the dropdown with administrative options\n and clicking Broadcast faculty, this will open a window prompting the user to enter a message to broadcast.\n Once the message is entered, click the Broadcast button to send it.\n
\n
\n \n Note: By using this function, only assistants and teachers can see the message.\n If you want everyone in the queue to see the message, use Broadcast as described above.\n \n
\n\n
Purge a queue
\n
\n When a queue is being purged, all people in the queue will be removed.\n Be careful though, as this action can not be reverted.\n
\n
\n To purge a queue, start off by clicking the dropdown with administrative options\n and then clicking the Purge queue button.\n Confirm in the popup that appears.\n
\n\n
Lock a queue
\n
\n When a queue is locked, users can see the queue but not join it.\n When locking a queue, users already\n in the queue will not be removed.\n
\n
\n To lock a queue start off by clicking the dropdown with administrative options\n and clicking Lock queue.\n Confirm in the popup that appears.\n
\n
\n To unlock a queue, perform the same procedure as above, but instead of choosing Lock, you choose Unlock.\n
\n\n
Set MOTD
\n
\n MOTD stands for Message Of The Day.\n If you want users to know something once they enter the room, you can set an MOTD.\n The MOTD will be shown to every user once they enter the room and may i.e.\n be used to let them know that you only accept presentations today.\n
\n
\n The MOTD may be set by entering the queue page and clicking the dropdown with\n administrative options and clicking Set MOTD. This will open a\n window prompting the user to enter a message, as well as showing the current\n MOTD if there is one. Once the message is entered, click the Set MOTD button to save it.\n
\n
\n In order to remove the MOTD, follow the same procedure, but click Remove MOTD on the window that opens up.\n
\n
\n \n Note: If you want the user to see the information for a longer amount of time,\n use Set queue info described below.\n \n
\n\n
Set queue info
\n
\n Information that you wish to show the user continuously should be entered as queue info.\n The queue info is shown on the top of the screen on the queue page if there is one,\n and can only be removed by a teacher.\n
\n
\n You set the queue info by clicking the dropdown with administrative options\n and clicking Set queue info, this will open a window prompting the user to enter some information.\n Once the message is entered, click the Set info button to save it.\n
\n
\n In order to remove the info, follow the same procedure, but click Remove info on the window that opens up.\n
\n
\n \n Note: If you only want the user to see the info once, when they enter the room, try\n using MOTD instead.\n \n
\n
\n : null\n );\n};\n","import React from 'react';\nimport { useSelector } from 'react-redux'\nimport { HashLink as Link } from 'react-router-hash-link';\nimport { GlobalStore } from '../../../store';\nimport User from '../../../models/User';\nimport Location from '../../../img/location.png';\nimport EnterQueue from '../../../img/enter-queue.png';\nimport { Lock, Star } from '../../../viewcomponents/FontAwesome';\n\nexport default (): JSX.Element | null => {\n\n const user = useSelector(store => store.user);\n\n return (\n user\n ?
\n
Users
\n\n
Join a queue
\n
\n To join a queue, you will first need to find the queue (see Find a queue).\n When you have found the queue, let's say you want to join the queue Inda,\n click the queue name.\n
\n
\n \n
\n
\n You now reach the queue page, where all the queueing takes place.\n If you are at a KTH computer, your\n location will be automatically filled in for you.\n
\n
\n \n
\n
\n If your location is not automatically filled in, please type your location in the location field.\n
\n
\n Now click the Join queue button, and you are done!\n
\n\n
Leave a queue
\n
\n To leave a queue, you must first join a queue.\n If you haven't joined the queue you want to leave,\n we recommend you to not join that queue.\n If you have already joined the queue you want to leave,\n go to the main page of the queue and click the Leave queue button.\n
\n\n
Receiving help
\n
\n Once you have joined a queue, you will notice that you are rewarded with a green\n button saying Receiving help, this button should be used if you are receiving help from an assistant\n who forgot to personally mark you as receiving help.\n If this is the case, click the button to prevent more assistants to come to you while you are receiving help.\n
\n\n
Colors
\n
\n When a person is standing in the queue, they may have different colored backgrounds, here are their respective description.\n
\n
White and Gray
\n
\n White and gray backgrounds indicates that the person is merely standing in the queue and the different colors are\n only used to make it easier to distinguish between the different rows.\n
\n
Golden
\n
\n If a preson has a golden star next to their name, that means that person is you.\n
\n
Green
\n
\n When a person has a green background color, that means there is currently an assistant at the person, whom is either helping or grading them.\n
\n
Red
\n
\n When a person has a red background color, that means an assistant has notified the person that they have to\n update their location since the assistant can't find them.\n
\n\n
Queue status
\n
\n The queues in Stay A While can have different statuses, which can be good to know.\n The statuses are indicated by the color and style of the queue name.\n
\n
\n
\n
Style
\n
Icon
\n
Description
\n
\n
\n
Active queue
\n
\n
The queue is active and can be joined
\n
\n
\n
Locked queue
\n
\n
The queue is locked and can't be joined
\n
\n
\n\n
Find a queue
\n
\n The first step to queueing would be to find the queue you wish to join, right? So how is that done?\n Well, first go to the Queues page.\n There you can either scroll to find the queue\n you are searching for, or you could search for the queue.\n To scroll, just scroll - more info on\n scrolling will not be covered in this help section.\n\n To search for a queue, type the queue name (or parts of the queue name) in the search box at the top of the page.\n
\n\n
Banners
\n
\n Although Stay A While is a flawless system, once in a blue moon, an administrator may want to inform the users of upcoming or current issues and changes.\n If that were to ever happen, an alert box will be displayed at the bottom of the user's screen.\n Feel free to discard the message after reading it thoroughly several times over.\n
\n
\n : null\n );\n};\n","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAa0AAABVCAYAAAD64Lv3AAAIUklEQVR4nO3dMUzi7gPGcTZHN0dHN0fH2xwdDQM3OOhiLnHQxcXFxZjc4mJiWByNiyacMcQoMeSC5oLGG4o0hKhgQ1RMwXTB4fkPBUUEf6DHce/f75N8FpBaqOlD376tARFCCCGGJNDrFSCEEELaDaVFCCHEmFBahBBCjAmlRQghxJhQWoQQQoxJy9KqVCrK5XKybVuWZQEA0DW2bSuXy6lSqXReWo+Pj0qn0yoUCiqVSvI8DwCArimVSnIcR+l0Wo+Pj52VVj6fV6FQ6PmbAAB8Lo7jKJ/Pd1Za5+fnenh46PnKAwA+l1KpJNu2Oysty7J6vuIAgM/JsixKCwBgBkoLAGAMSgsAYAxKCwBgDEoLAGAMSgsAYAxKCwBgDEoLAGAMSgsAYAxKCwBgDEoLAGAMSgsAYAxKCwBgDEoLAGAMSgsAYAxKCwBgDEoLAGAMSgsAYAxKCwBgDEoLAGAMSgsAYAxKCwDQUjZpa3LpUEPTexqci2t8PavEbe/Wh9ICADRRVmIrroFgRIEGfdPHWr/szXpRWgDwqbmyUo4Sl+6Lx4upMw01Kayn4lqwZTUs6/qyoFjqTtddXN9/p7QcW1+CEQVmzpSoezwW3vM/pImE1p1Wry9rY2lHgWBcK06T176wo/6JPQ0tJPR1M6OYU25j/craXY36rw/91HoPD40BoLmyYuF9v1AWs+0Vh3OhyZmdp33j0OqFstXndleiLQvLF9Niprase22s7KuvVmgzSW203F9/jDmlFYxoYOn5A23cWG+V1uDiqWbXz3zhU01+/6mR6eqGCu1pdMt5ewPfXmhs4rn0Rrbu/oE/UAB4Vvx9+nRk1F5plZsUU1Rfj8ryvHutLbxVWBEFgrsaP6r+7qNfr4YRB1byKnbhfRpSWvsamY8qEIxqPO42ef3bpTUabf4a6+hMX6b8D//L1k3Ldcv+iKsvGNHwalJfQq/XEQB6ynX0bSaivpmYhtouLbdpMX2J3ut5n/pWae3p229/Wdlo/PXzC5muDBMaUloxLSazGpuKKDD1S9uvhufeU1q+YsbSSCiiQCiutaaHszdanPMPhZcz91pb3FEgGNVksp1hRQDotrJi6/sKBPf17Sij0VD7w4PW1uHTkJ4/8nSo5eoEi+uDhPrfKq3ppHbd6rIuz/39aN1pmG6NSBlSWvuaTXnKHvxUfzCiwZWrhg3y/tLyz1f5P9fsQy4mkxqsnnTMep6u4/6G7F9qc8wYALqomDrTUCiiobCj4m22o9LyvHvtbh1rZGZPQ/PHWk7e1z13p7XF3ZZHWV+PXu5Xs0lL4/P7Gpw51PhWvsWpnI8zqrSeP8S9hiOdj5SWp+LRsfqDEfUtXTSMwbraWNpVILijsYPqMty8vk75R2YrXTrRCABtcQuandt5PupxOy2t/1r+ndbDcQ3UHUX1zyS0+KLc/i7DSsuTd2n755WmTxWrHZp+sLS82tTOefvltwOn+rumjrXtvl7u8Gbr82AA0F1lJdZj6qv/Et9Jabl32t481uh8TMNzvpGFY80e3LyeQOG6sjI3OnGa7UtdxX4kNbZw+LSc4fmf+rp59WpK/J9gXml5nk6q47BDYaf64X60tE41GIwosPCytE42Y/7sw1Xn5UbMWBqujunG3He8VwD4oGLqTMOhiAZX62bptV1arrabTWmfimsxea/ibUEbP870bTWp2R9XStTKynVl/c5qOZzUZPhMyweOsq6nbPJMIxOvhxEHXp3K+TgjS8vzbrQ8v1N9vKwPDw8eJNQXjKj/+1Xdxs9rcro6dhu/kXV5Vyev2bnqlM+msxkBoIvcghZrw4L1E9PaLq0rjTeWTOhQi6myskenGm5SQC0vMp5OaDnlqpiyGiZjRBSYONb2H37vhpaWJy9laTgUUd/cmRLuR0rL1Xr1vNVo9Hmc9jp+/PbMmdoGW8h07YQjADST2IypLxjV+MGNss79s0x1P7pg68S5V/a21SznC401FMzwekHFTOMswDZN/NTaZVmJ9f2GIkxo4w+/d3NL62k8d0fDmwVtfH9faV0nk/75rKljbTx9Y7nXysKOP20znNVG/KIJS6NT/lT456vCAaDb7rUy3+ZRUMsjroJmZ+p/9lDLl3V3/nmHgdW8ipfnGql/vAvXtBpcWt7TBXWBUEyjC7sdllZZVrx2GBzV2EHdbJjaOaupuusQmqh9qxgMO//AHzKAz6Es6/eVto+aiJ/6+665U20cXWk79cYsv9sb7dZel7zR9dM1qe80Z+nEc3WSrK1PXidduOWd2aXlvbx1SevbOCX1LXz6ZHIloZHp6vUHoT2N/aifLfN83dZ/zg50MtXZhb9ezC4EgJ740JT3xqOvDv2lOwUZX1r1JdPeDXMjCoR2/f8LE7a1e9kw5nt74Z+gnEi0cWPccnUGzo5GD5iQAaDHPlRaf+JIq/vv8d8pLQBAT1kHxxp8z0SM0J7GD/7OjcQpLQDAs9s7nWRulEgV2nKSuZH1F/9dE6UFADAGpQUAMAalBQAwBqUFADAGpQUAMAalBQAwBqUFADAGpQUAMAalBQAwBqUFADAGpQUAMAalBQAwBqUFADAGpQUAMAalBQAwBqUFADAGpQUAMAalBQAwBqUFADAGpQUAMAalBQAwBqUFADAGpQUAMAalBQAwRseldX5+roeHh56vOADgcymVSrJtu7PSyuVychyn5ysPAPhcHMdRPp/vrLQqlYrS6bQcx1GpVOr5mwAA/H8rlUpyHEfpdFqPj4+dlVatuHK5nGzblmVZAAB0jW3byuVyqlQqLQvrzdIihBBC/rVQWoQQQowJpUUIIcSYUFqEEEKMCaVFCCHEmFBahBBCjMn/AGFLW8FzAWLyAAAAAElFTkSuQmCC\"","export default \"data:image/png;base64,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\"","import React from 'react';\nimport { useSelector } from 'react-redux'\nimport { Link } from 'react-router-dom';\nimport { GlobalStore } from '../../../store';\nimport User from '../../../models/User';\n\nexport default (): JSX.Element | null => {\n\n const user = useSelector(store => store.user);\n\n return (\n !user\n ?
\n
Guests
\n
\n You are not logged in.\n
\n\n
\n If you only want to view a queue, that's cool,\n but to be able to join a queue, you will need to localStorage.setItem('LastVisitedUrl', window.location.pathname)}>log in.\n
\n\n
Viewing a queue
\n
\n If you don't want to log in, you can enter and check out any queue over at\n the home page to see the virtual line of eagerly waiting people.\n
\n
\n : null\n );\n};\n","import React, { useEffect } from 'react';\nimport { useDispatch } from 'react-redux'\nimport { resetTitle } from '../../actions/titleActions';\nimport AdministratorHelp from './Administrators';\nimport TeacherHelp from './Teachers';\nimport AssistantHelp from './Assistants';\nimport UserHelp from './Users';\nimport GuestHelp from './Guests';\n\nexport default (): JSX.Element => {\n\n const dispatch = useDispatch();\n useEffect(() => {\n if (!dispatch) {\n return;\n }\n\n dispatch(resetTitle());\n }, [dispatch]);\n\n return (\n
\n \t
Help and FAQ
\n \t
Have further questions or wish to create new queues? Contact: robertwb@kth.se
\n\n \n \n \n \n \n\n
\n)};\n","export function downloadFile(fileName: string, fileContent: string): void {\n\n function fakeClick(obj: any): void {\n const ev = document.createEvent(\"MouseEvents\");\n ev.initMouseEvent(\n \"click\",\n true,\n false,\n window,\n 0,\n 0,\n 0,\n 0,\n 0,\n false,\n false,\n false,\n false,\n 0,\n null\n );\n obj.dispatchEvent(ev);\n }\n\n function exportRaw(name: string, data: string): void {\n let urlObject = window.URL || window.webkitURL || window;\n let export_blob = new Blob([data]);\n\n if ('msSaveBlob' in navigator) {\n // Prefer msSaveBlob if available - Edge supports a[download] but\n // ignores the filename provided, using the blob UUID instead.\n // msSaveBlob will respect the provided filename\n navigator.msSaveBlob(export_blob, name);\n } else if ('download' in HTMLAnchorElement.prototype) {\n const saveLink = document.createElementNS(\n \"http://www.w3.org/1999/xhtml\",\n \"a\"\n ) as HTMLAnchorElement;\n saveLink.href = urlObject.createObjectURL(export_blob);\n saveLink.download = name;\n fakeClick(saveLink);\n } else {\n throw new Error(\"Neither a[download] nor msSaveBlob is available\");\n }\n }\n exportRaw(fileName, fileContent);\n }\n\nexport function copyToClipboard(content: string): void {\n const element = document.createElement('textarea');\n element.value = content;\n element.setAttribute('readonly', '');\n element.style.position = 'absolute';\n element.style.left = '-9999px';\n document.body.appendChild(element);\n element.select();\n document.execCommand('copy');\n document.body.removeChild(element);\n}\n\nexport function addFlashCard(message: string): void {\n\n const body = document.querySelector('body');\n if (body === null) {\n return;\n }\n\n const flashCard = document.createElement(\"div\");\n flashCard.className = \"flash-card\";\n flashCard.appendChild(document.createTextNode(message));\n\n body.appendChild(flashCard);\n flashCard.addEventListener('webkitAnimationEnd', () => {\n body.removeChild(flashCard);\n }, false);\n}\n","import React from 'react';\n\nexport default (props: any): JSX.Element | null => {\n\n const errorMessage = props.message;\n\n return (\n errorMessage\n ?
\n
\n {errorMessage}\n
\n
\n : null\n );\n};\n","import React from 'react';\nimport { Chart } from \"react-google-charts\";\n\nexport default (props: any): JSX.Element | null => {\n\n const header = [\n { type: 'date', label: 'Time' },\n 'Total',\n 'Help',\n 'Presentations'\n ];\n\n function formatData(): any[] {\n const result = props.data.map((item: any) => [\n new Date(Date.parse(item.time)),\n item.help_amount + item.present_amount,\n item.help_amount,\n item.present_amount\n ]);\n\n return result;\n }\n\n return (\n props.data\n ? Loading Chart}\n data={[\n header,\n ...formatData()\n ]}\n options={{\n hAxis: { title: 'Time', },\n vAxis: { title: 'Popularity', },\n series: { 1: { curveType: 'function' }, },\n }}\n rootProps={{ 'data-testid': '2' }} />\n : null\n );\n};\n","import React, { useState, useEffect } from 'react';\nimport { useSelector, useDispatch } from 'react-redux'\nimport axios from 'axios'\nimport moment from 'moment'\nimport DateTimePicker from 'react-datetime';\nimport { GlobalStore } from '../../store';\nimport { loadQueues } from '../../actions/queueActions';\nimport { resetTitle } from '../../actions/titleActions';\nimport { downloadFile, copyToClipboard, addFlashCard } from '../../utils/UtilityFunctions';\nimport PageNotFound from '../NoMatch';\nimport ErrorMessage from '../../viewcomponents/ErrorMessage';\nimport LineChart from '../../viewcomponents/LineChart';\nimport User from '../../models/User';\nimport { Copy, Download } from '../../viewcomponents/FontAwesome';\nimport { HTTP_SERVER_URL } from '../../configuration';\n\nexport default (): JSX.Element => {\n\n const user = useSelector(store => store.user);\n const queues = useSelector(store => store.queues.queueList.map(q => q.name))\n .sort((queue1, queue2) => queue1.toLowerCase() < queue2.toLowerCase() ? -1 : 1);\n\n const [statistics, setStatistics] = useState('');\n const [errorMessage, setErrorMessage] = useState('');\n const nintyDaysAgo = new Date();\n nintyDaysAgo.setDate(nintyDaysAgo.getDate() - 90);\n const [from, setFrom] = useState(Math.round(nintyDaysAgo.getTime() / 1000) as number | null);\n const [until, setUntil] = useState(Math.round(new Date().getTime() / 1000) as number | null);\n const [selectedQueue, setSelectedQueue] = useState(null as string | null);\n\n const dispatch = useDispatch();\n\n useEffect(() => {\n if (!dispatch) {\n return;\n }\n\n dispatch(loadQueues());\n dispatch(resetTitle());\n }, [dispatch]);\n\n function updateFrom(value: moment.Moment | string): void {\n try {\n const unix = (value as moment.Moment).unix();\n setFrom(unix);\n if (until !== null && unix > until) {\n setErrorMessage('From-date must be before until-date');\n }\n else {\n setErrorMessage('');\n }\n }\n catch {\n setFrom(null);\n }\n }\n\n function updateUntil(value: moment.Moment | string) {\n try {\n const unix = (value as moment.Moment).unix();\n setUntil(unix);\n if (from !== null && unix < from) {\n setErrorMessage('From-date must be before until-date');\n }\n else {\n setErrorMessage('');\n }\n }\n catch {\n setUntil(null);\n }\n }\n\n function isValidFromDate(time: moment.Moment): boolean {\n return (\n time.isAfter(moment().subtract(5, 'year'))\n && (until === null || time.isBefore(moment.unix(until as number)))\n && time.isBefore(moment())\n );\n }\n\n function isValidUntilDate(time: moment.Moment): boolean {\n return (\n time.isAfter(moment().subtract(5, 'year'))\n && (from === null || time.isAfter(moment.unix(from as number)))\n && time.isBefore(moment())\n );\n }\n\n function getStatistics() {\n if (user === null || selectedQueue === null || !(user.isAdministrator || user.isTeacherIn(selectedQueue))) {\n return;\n }\n\n if (errorMessage) {\n return;\n }\n\n axios.get(`${HTTP_SERVER_URL}/api/queues/${selectedQueue}/user_events${from !== null || until !== null ? '?' : ''}${from !== null ? `from=${from}` : ''}${until !== null ? `${from !== null && until !== null ? '&' : ''}until=${until}` : ''}`, {\n headers: { 'Authorization': `Token ${user.token}` }\n })\n .then(response => setStatistics(JSON.stringify(response.data, null, 2)))\n .catch(response => setStatistics(response.toString()));\n }\n\n function getHelpNumber() {\n return JSON.parse(statistics)\n .filter((entry: any) => entry.left_queue === true)\n .reduce((peopleHelped: number, entry: any) => entry.help === true ? peopleHelped + 1 : peopleHelped, 0) || 0;\n }\n\n function getPresentationNumber() {\n return JSON.parse(statistics)\n .filter((entry: any) => entry.left_queue === true)\n .reduce((peopleHelped: number, entry: any) => entry.help === false ? peopleHelped + 1 : peopleHelped, 0) || 0;\n }\n\n function getRemainingQueueLength() {\n const statisticsList = JSON.parse(statistics);\n\n if (statisticsList.length === 0) {\n return 0;\n }\n\n return statisticsList[statisticsList.length - 1].queue_length;\n }\n\n return (\n user === null\n ? \n :
\n >\n : \n No assistants?\n \n You must have plenty of time for your students ^.^\n \n );\n};\n","import React from 'react';\nimport { Link } from \"react-router-dom\";\nimport Queue from '../../../../models/Queue';\nimport AddAssistantViewComponent from './AddAssistant';\nimport AssistantListViewComponent from './AssistantList';\n\nexport default (props: any): JSX.Element => {\n\n const queue: Queue = props.queue;\n\n return (\n <>\n
\n
Assistants of Stay A While ?
\n
New assistants will have to log out and in again in order to get all of their new privileges.
\n
\n
\n \n
\n
\n
\n \n
\n
\n >\n );\n};\n","import React, { useState, useEffect } from 'react';\nimport { useSelector, useDispatch } from 'react-redux'\nimport { GlobalStore } from '../../../store';\nimport { loadAdditionalQueueData } from '../../../actions/administratorActions';\nimport { openShowQueueModal, openHideQueueModal, openDeleteQueueModal, openRenameQueueModal } from '../../../actions/modalActions';\nimport User from '../../../models/User';\nimport Queue from '../../../models/Queue';\nimport TeachersViewComponent from './Teachers/Teachers';\nimport AssistantsViewComponent from './Assistants/Assistants';\n\nexport default (): JSX.Element | null => {\n\n const user = useSelector(store => store.user);\n const queues = useSelector(store => store.queues.queueList)\n .sort((queue1: Queue, queue2: Queue) => queue1.name < queue2.name ? -1 : 1);\n\n const [selectedQueue, setSelectedQueue] = useState(null as Queue | null);\n const [selectedQueueName, setSelectedQueueName] = useState(null as string | null);\n\n function selectQueue(queueName: string): void {\n if (user !== null) {\n setSelectedQueue(queues.filter(q => q.name === queueName)[0] || null);\n setSelectedQueueName(queueName);\n dispatch(loadAdditionalQueueData(queueName, user.token));\n }\n }\n\n useEffect(() => {\n if (selectedQueueName !== null) {\n setSelectedQueue(queues.filter(q => q.name === selectedQueueName)[0] || null);\n }\n }, [queues, selectedQueueName]);\n\n const dispatch = useDispatch();\n\n return (\n queues.length === 0 || user === null\n ? null\n : <>\n
\n >\n }\n >\n );\n};\n","import React, { useEffect } from 'react';\nimport { Link } from 'react-router-dom';\nimport { useSelector, useDispatch } from 'react-redux'\nimport { GlobalStore } from '../../store';\nimport { loadAdministrators } from '../../actions/administratorActions';\nimport { enterAdminPage, leaveAdminPage } from '../../actions/pageActions';\nimport { resetTitle } from '../../actions/titleActions';\nimport User from '../../models/User';\nimport PageNotFound from '../NoMatch';\nimport AdministrationInformationViewComponent from './Administrators/AdministrationInformation';\nimport AdministratorsViewComponent from './Administrators/Administrators';\nimport QueuesViewComponent from './Queues/Queues';\nimport QueueOptionsViewComponent from './Queues/QueueOptions';\n\nexport default (): JSX.Element => {\n\n const user = useSelector(store => store.user);\n\n const dispatch = useDispatch();\n\n useEffect(() => {\n if (!dispatch) {\n return;\n }\n\n if (user && (user.isAdministrator || user.isTeacher())) {\n dispatch(enterAdminPage());\n dispatch(loadAdministrators(user?.token));\n dispatch(resetTitle());\n }\n\n return (() => {\n dispatch(leaveAdminPage());\n });\n }, [user, dispatch]);\n\n return (\n user === null || (!user.isAdministrator && !user.isTeacher())\n ? \n :
\n \n {\n user.isAdministrator\n ? <>\n \t\t
\n
\n
Administrators of Stay A While ?
\n
\n \t\t
\n \t\t \n \t\t
\n
\n
Queues of Stay A While
\n
\n \t\t
\n \t\t \n \t\t
\n \t\t
\n \t\t >\n : null\n }\n \n
\n );\n};\n","import React from 'react';\nimport { useSelector, useDispatch } from 'react-redux'\nimport { GlobalStore } from '../store';\nimport { closeModal, removeModal } from '../actions/modalActions';\nimport ModalInformation from '../models/Modal';\nimport Broadcast, { ModalType as BroadcastModal } from './Modals/BroadcastModal';\nimport BroadcastFaculty, { ModalType as BroadcastFacultyModal } from './Modals/BroadcastFacultyModal';\nimport DeleteQueue, { ModalType as DeleteQueueModal } from './Modals/DeleteQueueModal';\nimport HideQueue, { ModalType as HideQueueModal } from './Modals/HideQueueModal';\nimport PurgeQueue, { ModalType as PurgeQueueModal } from './Modals/PurgeQueueModal';\nimport RenameQueue, { ModalType as RenameQueueModal } from './Modals/RenameQueueModal';\nimport SendBadLocation, { ModalType as SendBadLocationModal } from './Modals/SendBadLocationModal';\nimport SendMessage, { ModalType as SendMessageModal } from './Modals/SendMessageModal';\nimport ServerMessage, { ModalType as ServerMessageModal } from './Modals/ServerMessageModal';\nimport AddBanner, { ModalType as AddBannerModal } from './Modals/AddBannerModal';\nimport UpdateBanner, { ModalType as UpdateBannerModal } from './Modals/UpdateBannerModal';\nimport SetMotd, { ModalType as SetMotdModal } from './Modals/SetMotdModal';\nimport SetQueueInformation, { ModalType as SetQueueInformationModal } from './Modals/SetQueueInformationModal';\nimport ShowMessage, { ModalType as ShowMessageModal } from './Modals/ShowMessageModal';\nimport ShowMotd, { ModalType as ShowMotdModal } from './Modals/ShowMotdModal';\nimport ShowQueue, { ModalType as ShowQueueModal } from './Modals/ShowQueueModal';\n\nexport default (): JSX.Element => {\n\n const modals = useSelector(store => store.modals);\n\n const dispatch = useDispatch();\n\n function onHide(): void {\n dispatch(closeModal());\n setTimeout(() => dispatch(removeModal()), 1000);\n }\n\n function toJSX(modal: ModalInformation, isVisible: boolean): JSX.Element | null {\n const props = {\n ...modal.modalData,\n show: isVisible,\n onHide: isVisible ? () => onHide() : undefined\n };\n\n switch (modal.modalType) {\n\n case BroadcastModal: {\n return ();\n }\n\n case BroadcastFacultyModal: {\n return ();\n }\n\n case DeleteQueueModal: {\n return ();\n }\n\n case HideQueueModal: {\n return ();\n }\n\n case PurgeQueueModal: {\n return ();\n }\n\n case RenameQueueModal: {\n return ();\n }\n\n case SendBadLocationModal: {\n return ();\n }\n\n case SendMessageModal: {\n return ();\n }\n\n case ServerMessageModal: {\n return ();\n }\n\n case AddBannerModal: {\n return ();\n }\n\n case UpdateBannerModal: {\n return ();\n }\n\n case SetMotdModal: {\n return ();\n }\n\n case SetQueueInformationModal: {\n return ();\n }\n\n case ShowMessageModal: {\n return ();\n }\n\n case ShowMotdModal: {\n return ();\n }\n\n case ShowQueueModal: {\n return ();\n }\n\n }\n\n return null;\n }\n\n return (\n <>\n {\n modals.modalList.map((modal, index) =>\n
\n { toJSX(modal, index === modals.current) }\n
\n )\n }\n >\n );\n};\n","import React, { useEffect } from 'react';\nimport { BrowserRouter as Router, Switch, Route } from \"react-router-dom\";\nimport { useSelector, useDispatch } from 'react-redux'\nimport { useAlert } from 'react-alert';\nimport { loadQueues } from './actions/queueActions';\nimport { showBanner, hideBanner, triggerBannerRedraw } from './actions/bannerActions';\nimport HomePage from './pages/Home';\nimport Queue from './pages/Queue';\nimport NavBar from './viewcomponents/NavBar';\nimport AboutPage from './pages/About';\nimport HelpPage from './pages/Help';\nimport StatisticsPage from './pages/Statistics';\nimport PageNotFound from './pages/NoMatch';\nimport LoginPage from './pages/MockLogin';\nimport LogoutPage from './pages/Logout';\nimport AdministrationPage from './pages/Administration';\nimport Modal from './viewcomponents/Modal';\nimport { GlobalStore } from './store';\nimport Banner from './models/Banner';\n\nexport default (): JSX.Element => {\n\n const alert = useAlert();\n const banners = useSelector(store => store.banners.banners);\n const bannerRedrawTrigger = useSelector(store => store.banners.redrawTrigger);\n\n const dispatch = useDispatch();\n dispatch(loadQueues());\n\n useEffect(() => {\n let nextBanner = -1;\n\n const seenBanners = JSON.parse(localStorage.getItem('SeenBanners') ?? '[]') as number[];\n for (let banner of banners) {\n if (banner.isShowing || banner.endTime < Date.now() || seenBanners.some(id => id === banner.id)) {\n continue;\n }\n\n if (banner.startTime > Date.now()) {\n nextBanner = nextBanner === -1 ? banner.startTime : Math.min(nextBanner, banner.startTime);\n continue;\n }\n\n console.log(JSON.stringify(banner));\n alert.show(banner.message,\n {\n onOpen: () => dispatch(showBanner(banner.id)),\n onClose: () => dispatch(hideBanner(banner.id))\n });\n }\n\n if (nextBanner !== -1) {\n const timeoutId = setTimeout(() => { dispatch(triggerBannerRedraw()); }, nextBanner - Date.now());\n return () => { clearTimeout(timeoutId) };\n }\n }, [banners, bannerRedrawTrigger]);\n\n return (\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n )\n};\n","import React from 'react'\nimport { useSelector } from 'react-redux'\nimport { Helmet } from 'react-helmet';\nimport { GlobalStore } from '../store';\n\nexport default (): JSX.Element => {\n\n const title = useSelector(store => store.title);\n\n return (\n \n { title }\n \n )\n};\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\ntype Config = {\n onSuccess?: (registration: ServiceWorkerRegistration) => void;\n onUpdate?: (registration: ServiceWorkerRegistration) => void;\n};\n\nexport function register(config?: Config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(\n process.env.PUBLIC_URL,\n window.location.href\n );\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n // console.log(\n // 'This web app is being served cache-first by a service ' +\n // 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n // );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl: string, config?: Config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n // console.log(\n // 'New content is available and will be used when all ' +\n // 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n // );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n // console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n // console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl: string, config?: Config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' }\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n // console.log(\n // 'No internet connection found. App is running in offline mode.'\n // );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready\n .then(registration => {\n registration.unregister();\n })\n .catch(error => {\n // console.error(error.message);\n });\n }\n}\n","import React, { useEffect } from 'react';\nimport ReactDOM from 'react-dom';\nimport { Provider } from 'react-redux';\nimport { useDispatch } from 'react-redux'\nimport store from './store';\nimport App from './App';\nimport Helmet from './viewcomponents/Helmet';\nimport { positions, Provider as AlertProvider } from 'react-alert';\nimport AlertTemplate from 'react-alert-template-basic';\nimport * as serviceWorker from './serviceWorker';\nimport { loadQueues } from './actions/queueActions';\nimport { loadBanners } from './actions/bannerActions';\nimport { initialize } from './actions/globalActions';\nimport { loadUser } from './actions/userActions';\n\nconst alertOptions = {\n timeout: 0,\n position: positions.BOTTOM_CENTER\n};\n\nfunction LifeCycle() {\n const dispatch = useDispatch();\n\n useEffect(() => {\n if (!dispatch) {\n return;\n }\n\n dispatch(loadUser());\n dispatch(loadQueues());\n dispatch(loadBanners());\n dispatch(initialize());\n }, [dispatch]);\n\n return (\n \n );\n}\n\nReactDOM.render(\n \n \n \n \n \n \n \n ,\n document.getElementById('root')\n);\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""}