302 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			302 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<!DOCTYPE html>
 | 
						|
<html>
 | 
						|
	<head>
 | 
						|
		<title>FTracker</title>
 | 
						|
		<meta name="theme-color" content="#c50e1f">
 | 
						|
		<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
 | 
						|
		<style>
 | 
						|
			html, body {
 | 
						|
				margin: 0;
 | 
						|
				padding: 0;
 | 
						|
				height: 100%;
 | 
						|
				background: #ddd;
 | 
						|
				font-family: sans-serif;
 | 
						|
			}
 | 
						|
			h1 {
 | 
						|
				margin: 0;
 | 
						|
				padding: 16px;
 | 
						|
				text-transform: uppercase;
 | 
						|
				color: #eee;
 | 
						|
				background: #c50e1f;
 | 
						|
				text-align: center;
 | 
						|
			}
 | 
						|
			form {
 | 
						|
				padding: 16px;
 | 
						|
			}
 | 
						|
			label {
 | 
						|
				display: block;
 | 
						|
				font-size: 16px;
 | 
						|
				margin-bottom: 16px;
 | 
						|
				color: #444;
 | 
						|
			}
 | 
						|
			label#agreelabel {
 | 
						|
				height: 32px;
 | 
						|
				line-height: 32px;
 | 
						|
			}
 | 
						|
			label span {
 | 
						|
				width: calc(100% - 50px);
 | 
						|
				display: inline-block;
 | 
						|
				vertical-align: middle;
 | 
						|
				line-height: normal;
 | 
						|
			}
 | 
						|
			input {
 | 
						|
				border: none;
 | 
						|
				padding: 16px;
 | 
						|
				margin: 4px 0;
 | 
						|
				font-size: 16px;
 | 
						|
			}
 | 
						|
			input[type=text] {
 | 
						|
				color: #000;
 | 
						|
				width: calc(100% - 32px);
 | 
						|
			}
 | 
						|
			input[type=submit] {
 | 
						|
				background: #c50e1f;
 | 
						|
				text-transform: uppercase;
 | 
						|
				font-weight: bold;
 | 
						|
				color: #fff;
 | 
						|
				width: 100%;
 | 
						|
			}
 | 
						|
			input[type=checkbox] {
 | 
						|
				transform: translateY(-3px);
 | 
						|
				float: left;
 | 
						|
				height: 32px;
 | 
						|
				width: 32px;
 | 
						|
				margin-right: 8px;
 | 
						|
			}
 | 
						|
			.request {
 | 
						|
				display: block;
 | 
						|
				position: fixed;
 | 
						|
				background: #ddd;
 | 
						|
				top: 16px;
 | 
						|
				left: 16px;
 | 
						|
				width: calc(100% - 32px);
 | 
						|
				box-shadow: 0 1px 4px 0;
 | 
						|
			}
 | 
						|
			input[type=datetime-local] {
 | 
						|
				width: calc(100% - 24px);
 | 
						|
				padding: 12px;
 | 
						|
				font-size: 12px;
 | 
						|
				background: #fff;
 | 
						|
			}
 | 
						|
		</style>
 | 
						|
		<script>
 | 
						|
			// 1st script, prepares values needed for writing document
 | 
						|
			var cbt = {
 | 
						|
				'arrival': 'I have read and will adhere to the <a href="/guidelines" target="_blank">protection guidelines</a>',
 | 
						|
				'departure': 'I have cleaned my workspace'
 | 
						|
			}
 | 
						|
			function getParams() {
 | 
						|
				var h = document.location.href
 | 
						|
				var qparam = h.split('?')[1] || null
 | 
						|
				if (qparam == null)
 | 
						|
					return null
 | 
						|
				var vals = qparam.split('=')
 | 
						|
				if (vals.length < 2 || !cbt.hasOwnProperty(vals[0]))
 | 
						|
					return null
 | 
						|
				return {
 | 
						|
					action: vals[0],
 | 
						|
					room: vals[1]
 | 
						|
				}
 | 
						|
			}
 | 
						|
			var qp = getParams()
 | 
						|
		</script>
 | 
						|
	</head>
 | 
						|
	<body>
 | 
						|
		<h1><script>
 | 
						|
			if (qp)
 | 
						|
				document.write(qp.action + "<br>Room " + qp.room)
 | 
						|
			else
 | 
						|
				document.write('FTracker<br>V1')
 | 
						|
		</script></h1>
 | 
						|
		<form id="mainform">
 | 
						|
			<label>
 | 
						|
				Full Name:<br>
 | 
						|
				<input type="text" name="name" id="name" placeholder="John Doe" required>
 | 
						|
			</label>
 | 
						|
			<label id="agreelabel">
 | 
						|
				<input type="checkbox" name="agree" id="agree" required>
 | 
						|
				<span><script>
 | 
						|
					document.write(qp ? cbt[qp.action] : '')
 | 
						|
				</script></span>
 | 
						|
			</label>
 | 
						|
			<input type="submit">
 | 
						|
		</form>
 | 
						|
		<script>
 | 
						|
			var mform = document.getElementById('mainform')
 | 
						|
			if (qp == null) {
 | 
						|
				mform.innerHTML = 'This is a web app to track which people\
 | 
						|
				were in the same rooms at which times in order to backtrace\
 | 
						|
				potential viral infections.<br><br>\
 | 
						|
				If you\'ve reached this page that either means your\'re\
 | 
						|
				testing things or something has gone quite wrong with the\
 | 
						|
				URL.<br>\
 | 
						|
				In the former case: Yay it works! In the latter you should\
 | 
						|
				probably contact an admin or a dev nearby :(<br><br>\
 | 
						|
				Here are a few links for testing:<br>\
 | 
						|
				<a href="/view">View Data</a>, \
 | 
						|
				<a href="/?arrival=42">Test Arrival</a>, \
 | 
						|
				<a href="/?departure=42">Test Departure</a><br><br>\
 | 
						|
				© 2020 made by <a target="_blank" href="mailto:o.winkels@fasttube.de">Oskar</a> \
 | 
						|
				for <a target="_blank" href="//fasttube.de">FaSTTUBe</a>. \
 | 
						|
				For source code & licensing see <a href="//git.fasttube.de/FaSTTUBe/ftracker">git repo</a>'
 | 
						|
			}
 | 
						|
 | 
						|
			// Prefill the name field if it was successfully entered before
 | 
						|
			var savedName = localStorage.getItem('name')
 | 
						|
			if (savedName && qp)
 | 
						|
				document.getElementById('name').value = savedName
 | 
						|
 | 
						|
			// 2nd script, server API communication
 | 
						|
			var name, agreed
 | 
						|
			mform.onsubmit = function(e) {
 | 
						|
 | 
						|
				e.preventDefault()
 | 
						|
 | 
						|
				name = e.srcElement[0].value
 | 
						|
				agreed = e.srcElement[1].checked
 | 
						|
 | 
						|
				sendMainData()
 | 
						|
 | 
						|
			}
 | 
						|
 | 
						|
			function sendMainData() {
 | 
						|
 | 
						|
				// POST JSON. See docs/API.md
 | 
						|
				var payload = (qp.action == 'arrival') ?
 | 
						|
					{
 | 
						|
						'room': qp.room,
 | 
						|
						'name': name,
 | 
						|
						'agreetoguidelines': agreed
 | 
						|
					} :
 | 
						|
					{
 | 
						|
						'name': name,
 | 
						|
						'cleanedworkspace': agreed
 | 
						|
					}
 | 
						|
 | 
						|
				post("/" + qp.action, payload)
 | 
						|
 | 
						|
			}
 | 
						|
 | 
						|
			function post(url, payload) {
 | 
						|
 | 
						|
				console.log("Sending payload:", payload)
 | 
						|
 | 
						|
				return fetch(url, {
 | 
						|
					method: "POST",
 | 
						|
					body: JSON.stringify(payload)
 | 
						|
				}).then(res => {
 | 
						|
					handleResponse(res)
 | 
						|
				})
 | 
						|
 | 
						|
			}
 | 
						|
 | 
						|
			function handleResponse(res) {
 | 
						|
 | 
						|
				console.log("Request complete! response:", res)
 | 
						|
 | 
						|
				if (Math.floor(res.status / 100) == 2) {
 | 
						|
 | 
						|
					// Success
 | 
						|
					mform = document.getElementById('mainform')
 | 
						|
					mform.innerHTML = "<h2>Done. Thanks!</h2>"
 | 
						|
					localStorage.setItem('name', name)
 | 
						|
 | 
						|
				} else if (res.status == 409) {
 | 
						|
 | 
						|
					// Conflict, more data requested
 | 
						|
					handleRequest(res)
 | 
						|
 | 
						|
				} else {
 | 
						|
 | 
						|
					// Any other generic error
 | 
						|
					res.text().then(function (text) {
 | 
						|
						alert(text)
 | 
						|
					})
 | 
						|
 | 
						|
				}
 | 
						|
 | 
						|
			}
 | 
						|
 | 
						|
			function handleRequestSubmit(e, json) {
 | 
						|
 | 
						|
				e.preventDefault()
 | 
						|
 | 
						|
				var input = e.srcElement[0].value
 | 
						|
				var iso = new Date(input).toISOString()
 | 
						|
 | 
						|
				// POST JSON. See docs/API.md
 | 
						|
				var payload = (json.request == 'arrival') ?
 | 
						|
					{
 | 
						|
						'room': qp.room,
 | 
						|
						'name': name,
 | 
						|
						'arrival': iso,
 | 
						|
						'agreetoguidelines': agreed
 | 
						|
					} :
 | 
						|
					{
 | 
						|
						'name': name,
 | 
						|
						'departure': iso,
 | 
						|
						'cleanedworkspace': agreed
 | 
						|
					}
 | 
						|
 | 
						|
				return post("/" + json.request, payload)
 | 
						|
 | 
						|
			}
 | 
						|
 | 
						|
			function localISOTimeMinutes(date) {
 | 
						|
 | 
						|
				var tzoffset = date.getTimezoneOffset() * 60000; //offset in milliseconds
 | 
						|
				var localISOTime = (new Date(date - tzoffset)).toISOString().slice(0, -1);
 | 
						|
 | 
						|
				return localISOTime.split(':').slice(0,2).join(':')
 | 
						|
 | 
						|
			}
 | 
						|
 | 
						|
			function handleRequest(res) {
 | 
						|
 | 
						|
				var reqt = {
 | 
						|
					'arrival': 'You probably forgot to sign in when you arrived. Please enter your arrival time now:',
 | 
						|
					'departure': 'You probably forgot to sign out when you left. Please enter your departure time now:'
 | 
						|
				}
 | 
						|
 | 
						|
				mform.innerHTML = "<h2>Processing Request...</h2>"
 | 
						|
 | 
						|
				res.json().then(function (json) {
 | 
						|
 | 
						|
					var aInfo = ''
 | 
						|
					var minD = ''
 | 
						|
					if (json.request == 'departure') {
 | 
						|
						var d = new Date(json.arrival.time)
 | 
						|
						var dInfo = d.toString('en-GB').split(' ').slice(0,5).join(' ')
 | 
						|
						aInfo = `Your last arrival was on <b>${dInfo}</b> in room <b>${json.arrival.room}</b>.`
 | 
						|
						minD = `min="${localISOTimeMinutes(d)}"`
 | 
						|
					}
 | 
						|
 | 
						|
					var now = localISOTimeMinutes(new Date())
 | 
						|
 | 
						|
					document.body.innerHTML +=
 | 
						|
						`<div class="request">
 | 
						|
							<h1>${json.request} missing!</h1>
 | 
						|
							<form id="reqform">
 | 
						|
								<label>
 | 
						|
									${reqt[json.request]}
 | 
						|
									<input type="datetime-local" id="datetime" ${minD} max="${now}" required>
 | 
						|
									${aInfo}
 | 
						|
								</label>
 | 
						|
								<input type="submit">
 | 
						|
							</form>
 | 
						|
						</div>`
 | 
						|
 | 
						|
					rform = document.getElementById('reqform')
 | 
						|
					rform.onsubmit = async function(e) {
 | 
						|
						await handleRequestSubmit(e, json)
 | 
						|
						document.querySelector('.request').remove()
 | 
						|
						setTimeout(sendMainData, 200)
 | 
						|
					}
 | 
						|
 | 
						|
				})
 | 
						|
 | 
						|
			}
 | 
						|
		</script>
 | 
						|
	</body>
 | 
						|
</html>
 |