owlboard-svelte/src/lib/ldb/staff/train-detail.svelte

278 lines
7.0 KiB
Svelte

<script>
import OverlayIsland from '$lib/islands/overlay-island.svelte';
import { fade } from 'svelte/transition';
import Reason from '$lib/raw-fetchers/reason.svelte';
import { uuid } from '$lib/stores/uuid';
export let detail = {
uid: '',
rid: '',
headcode: '',
show: true
};
export let close;
function handleClick() {
close();
}
async function getTrain(rid) {
try {
console.log(`Requested Station: ${rid}`);
const url = `https://owlboard.info/api/v2/live/train/rid/${rid}`;
const opt = {
method: 'GET',
headers: {
uuid: $uuid
}
};
const data = await fetch(url, opt);
return await data.json();
} catch (error) {
console.error('Error fetching data:', error);
}
}
async function parseDelay(location) {
let string, state;
if (location?.lateness) {
try {
const result = Math.floor(location.lateness / 60);
if (result === 0) {
(string = 'RT'), (state = '');
} else if (result < 0) {
(string = -result + 'E'), (state = 'early');
} else if (result > 0) {
(string = result + 'L'), (state = 'late');
}
} catch {
(string = ''), (state = '');
}
} else if (location.arrivalType === 'Delayed') {
(string = ''), (state = 'late');
} else {
(string = ''), (state = 'noreport');
}
return {
string: string,
state: state
};
}
function parseTime(date) {
const parsedTime = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
return parsedTime !== 'Invalid Date' ? parsedTime : null;
}
function parseTimes(service) {
const sta = new Date(service.sta),
eta = new Date(service.eta),
ata = new Date(service.ata);
const std = new Date(service.std),
etd = new Date(service.etd),
atd = new Date(service.atd);
let parsedSta = parseTime(sta),
parsedEta = parseTime(eta),
parsedAta = parseTime(ata);
let parsedStd = parseTime(std),
parsedEtd = parseTime(etd),
parsedAtd = parseTime(atd);
if (service.isCancelled) {
(parsedEta = 'CANC'), (parsedEtd = 'CANC');
}
let times = {
sta: parsedSta || '-',
eata: parsedEta || parsedAta || '-',
aEst: parsedEta ? 'estimate' : '',
std: parsedStd || '-',
eatd: parsedEtd || parsedAtd || '-',
dEst: parsedEtd ? 'estimate' : ''
};
if (service.isCancelled) {
(parsedEta = 'CANC'), (parsedEtd = 'CANC');
(times.aEst = 'canc'), (times.dEst = 'canc');
}
return times;
}
</script>
<OverlayIsland>
<div id="detailBox">
<button type="button" id="closeService" on:click={handleClick}>X</button>
{#await getTrain(detail.rid)}
<h6>{detail.headcode}</h6>
<p in:fade id="loading">Loading Data...</p>
{:then train}
<h6>{train.GetServiceDetailsResult.operatorCode}: {detail.headcode}</h6>
<p>
Locations in grey are not scheduled stops
<br />
Times in <span class="estimate">yellow</span> are estimated times
</p>
{#if train.GetServiceDetailsResult.delayReason}
<p class="reason late">
<Reason type="delay" code={train.GetServiceDetailsResult.delayReason} />
</p>
{/if}
{#if train.GetServiceDetailsResult.cancelReason}
<p class="reason canc">
<Reason type="cancel" code={train.GetServiceDetailsResult.cancelReason} />
</p>
{/if}
<table id="detailTable">
<tr>
<th class="tableLocation">Loc.</th>
<th class="tablePlatform">Pl.</th>
<th class="tableTime">Sch</th>
<th class="tableTime">Est/<br>Act</th>
<th class="tableTime">Sch</th>
<th class="tableTime">Est/<br>Act</th>
<th class="tableDelay" />
</tr>
<tr>
<th colspan="2" />
<th colspan="2">Arrival</th>
<th colspan="2">Departure</th>
<th />
</tr>
{#each train.GetServiceDetailsResult.locations.location as location}
<tr>
<td class="location {location?.isPass === 'true' ? 'pass' : ''}">{location.tiploc}</td>
<td class={location?.isPass === 'true' ? 'pass' : ''}>{location.platform || ''}</td>
{#await parseTimes(location)}
<td />
<td />
<td />
<td />
{:then times}
<td class={location?.isPass === 'true' ? 'pass' : ''}>{times.sta}</td>
<td class="{location?.isPass === 'true' ? 'pass' : ''} {times.aEst}">{times.eata}</td>
<td class={location?.isPass === 'true' ? 'pass' : ''}>{times.std}</td>
<td class="{location?.isPass === 'true' ? 'pass' : ''} {times.dEst}">{times.eatd}</td>
{/await}
{#await parseDelay(location)}
<td>-</td>
{:then delay}
<td class={delay.state}>{delay.string}</td>
{/await}
</tr>
{/each}
</table>
{:catch}
<h6>Error loading data</h6>
{/await}
</div>
</OverlayIsland>
<style>
#detailBox {
width: 100%;
min-height: 100px;
overflow-x: hidden;
overflow-y: auto;
}
h6 {
position: absolute;
top: -25px;
left: 20px;
font-size: 18px;
}
#loading {
color: white;
animation: pulse-early 2.5s linear infinite;
}
p {
margin-top: 45px;
margin-bottom: 0px;
}
p.reason {
margin-top: 5px;
}
#closeService {
position: absolute;
top: 10px;
right: 10px;
border: none;
border-radius: 60px;
width: 35px;
height: 35px;
background-color: var(--main-bg-color);
color: white;
font-weight: 700;
font-size: 14px;
}
#detailTable {
margin-top: 10px;
table-layout: fixed;
width: 100%;
margin-top: 12px;
margin-left: 0px;
margin-right: 0px;
padding: 0px;
color: white;
font-family: ubuntu,monospace;
font-size: 16px;
}
@media screen and (max-width: 338px) {
#detailTable{font-size: 14px}
}
@media screen and (max-width: 301px) {
#detailTable{font-size: 12px}
}
@media screen and (min-width: 469px) {
#detailTable{font-size: 20px}
}
.tableLocation {
width: 18%;
}
td.location {
color: yellow;
}
.tablePlatform {
width: 5%;
}
.tableTime {
width: 15%;
}
.tableDelay {
width: 7%;
}
.estimate {
color: rgb(255, 255, 119);
}
.pass {
color: white !important;
opacity: 0.45;
}
.canc {
color: white;
animation: pulse-cancel 1.5s linear infinite;
}
.early {
animation: pulse-early 1.5s linear infinite;
}
.late {
color: white;
animation: pulse-late 1.5s linear infinite;
}
@keyframes pulse-late {
50% {
color: var(--main-warning-color);
}
}
@keyframes pulse-cancel {
50% {
color: var(--main-alert-color);
}
}
@keyframes pulse-early {
50% {
color: rgb(136, 164, 255);
}
}
</style>