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

287 lines
8.5 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";
import StylesToc from "$lib/train/styles-toc.svelte";
import { getApiUrl } from "$lib/scripts/upstream";
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 = `${getApiUrl()}/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><StylesToc toc={train.GetServiceDetailsResult.operatorCode} full={true} /> {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: -16px;
left: 20px;
font-size: 14px;
color: whitesmoke;
}
#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: 9%;
}
.tableTime {
width: 14%;
}
.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>