Compare commits
No commits in common. "main" and "2024.07.4" have entirely different histories.
@ -36,10 +36,10 @@ http {
|
|||||||
#server owlboard-backend:8460;
|
#server owlboard-backend:8460;
|
||||||
|
|
||||||
# External to Kubernetes:
|
# External to Kubernetes:
|
||||||
#server 172.30.129.19:8460;
|
server 172.30.129.19:8460;
|
||||||
|
|
||||||
# Within Docker:
|
# Within Docker:
|
||||||
server backend:8460;
|
#server owlboard-backend:8460
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
|
18
package-lock.json
generated
@ -7,12 +7,14 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "owlboard-svelte",
|
"name": "owlboard-svelte",
|
||||||
"version": "2024.03.2",
|
"version": "2024.03.2",
|
||||||
|
"dependencies": {
|
||||||
|
"@tabler/icons-svelte": "^3.2.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@owlboard/ts-types": "^1.2.1",
|
"@owlboard/ts-types": "^1.2.0",
|
||||||
"@sveltejs/adapter-auto": "^2.0.0",
|
"@sveltejs/adapter-auto": "^2.0.0",
|
||||||
"@sveltejs/adapter-static": "^2.0.2",
|
"@sveltejs/adapter-static": "^2.0.2",
|
||||||
"@sveltejs/kit": "^1.5.0",
|
"@sveltejs/kit": "^1.5.0",
|
||||||
"@tabler/icons-svelte": "^3.2.0",
|
|
||||||
"eslint": "^8.28.0",
|
"eslint": "^8.28.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-svelte": "^2.26.0",
|
"eslint-plugin-svelte": "^2.26.0",
|
||||||
@ -587,11 +589,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@owlboard/ts-types": {
|
"node_modules/@owlboard/ts-types": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.0",
|
||||||
"resolved": "https://git.fjla.uk/api/packages/OwlBoard/npm/%40owlboard%2Fts-types/-/1.2.1/ts-types-1.2.1.tgz",
|
"resolved": "https://git.fjla.uk/api/packages/OwlBoard/npm/%40owlboard%2Fts-types/-/1.2.0/ts-types-1.2.0.tgz",
|
||||||
"integrity": "sha512-3iLFBPmLblQiksvGciPxmnZ+1kvywYDH0Qb8BIY33tZqmkY+/IccqoaxLICRrVPzDo87YkiMwsjorHloxlXJog==",
|
"integrity": "sha512-9xu7WJ+6eIiz6frd1O3/LGLrD4wAr16tI1Xd2WTkke8VONEm28f8T5M5J68pXPddHXOygXVkHUUFjAbTYrS+7Q==",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"license": "GPL-3.0-or-later"
|
|
||||||
},
|
},
|
||||||
"node_modules/@polka/url": {
|
"node_modules/@polka/url": {
|
||||||
"version": "1.0.0-next.25",
|
"version": "1.0.0-next.25",
|
||||||
@ -695,7 +696,6 @@
|
|||||||
"version": "3.7.0",
|
"version": "3.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.7.0.tgz",
|
||||||
"integrity": "sha512-lJGIZLSWrPO6VygRUbaVvQjWgL2EaiBMD8e6leCYUQ8ZPO4LIzKMq358C8KlhXJcyNiRz1Io3YWoc/DNTcWqSg==",
|
"integrity": "sha512-lJGIZLSWrPO6VygRUbaVvQjWgL2EaiBMD8e6leCYUQ8ZPO4LIzKMq358C8KlhXJcyNiRz1Io3YWoc/DNTcWqSg==",
|
||||||
"dev": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/codecalm"
|
"url": "https://github.com/sponsors/codecalm"
|
||||||
@ -705,7 +705,6 @@
|
|||||||
"version": "3.7.0",
|
"version": "3.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tabler/icons-svelte/-/icons-svelte-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tabler/icons-svelte/-/icons-svelte-3.7.0.tgz",
|
||||||
"integrity": "sha512-G8/SINJ4sRxICHJMbQaLH2FWJZPFns4N383wvw2LQ7lQUT8NhhsKjK/i6LxyLZtyEjmVyGaEKpBLdz3SWldgBA==",
|
"integrity": "sha512-G8/SINJ4sRxICHJMbQaLH2FWJZPFns4N383wvw2LQ7lQUT8NhhsKjK/i6LxyLZtyEjmVyGaEKpBLdz3SWldgBA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tabler/icons": "3.7.0"
|
"@tabler/icons": "3.7.0"
|
||||||
},
|
},
|
||||||
@ -2436,7 +2435,6 @@
|
|||||||
"version": "3.59.2",
|
"version": "3.59.2",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz",
|
||||||
"integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==",
|
"integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
|
12
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "owlboard-svelte",
|
"name": "owlboard-svelte",
|
||||||
"version": "2024.11.4",
|
"version": "2024.03.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
@ -13,7 +13,7 @@
|
|||||||
"format": "prettier --plugin-search-dir . --write ."
|
"format": "prettier --plugin-search-dir . --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@owlboard/ts-types": "^1.2.1",
|
"@owlboard/ts-types": "^1.2.0",
|
||||||
"@sveltejs/adapter-auto": "^2.0.0",
|
"@sveltejs/adapter-auto": "^2.0.0",
|
||||||
"@sveltejs/adapter-static": "^2.0.2",
|
"@sveltejs/adapter-static": "^2.0.2",
|
||||||
"@sveltejs/kit": "^1.5.0",
|
"@sveltejs/kit": "^1.5.0",
|
||||||
@ -27,8 +27,10 @@
|
|||||||
"svelte-french-toast": "^1.2.0",
|
"svelte-french-toast": "^1.2.0",
|
||||||
"svelte-sitemap": "^2.6.0",
|
"svelte-sitemap": "^2.6.0",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^4.3.0",
|
"vite": "^4.3.0"
|
||||||
"@tabler/icons-svelte": "^3.2.0"
|
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"@tabler/icons-svelte": "^3.2.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onDestroy } from "svelte";
|
import { onMount, onDestroy } from "svelte";
|
||||||
export let text: string;
|
export let text: string;
|
||||||
|
|
||||||
let isVisible: boolean = false;
|
let isVisible: boolean = false;
|
||||||
@ -44,16 +44,15 @@
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
top: 50%;
|
bottom: 125%;
|
||||||
right: 125%;
|
left: 50%;
|
||||||
transform: translateY(-50%);
|
|
||||||
margin-left: -60px;
|
margin-left: -60px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.5s;
|
transition: opacity 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip:hover .tooltiptext {
|
.tooltip:hover .tooltiptext {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 0.8;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
interface CardConfig {
|
export interface CardConfig {
|
||||||
title: string;
|
title: string;
|
||||||
showHelp: boolean;
|
showHelp: boolean;
|
||||||
showRefresh: boolean;
|
showRefresh: boolean;
|
||||||
@ -7,7 +7,7 @@ interface CardConfig {
|
|||||||
refreshing: boolean;
|
refreshing: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LookupCardConfig {
|
export interface LookupCardConfig {
|
||||||
title: string;
|
title: string;
|
||||||
formAction: string;
|
formAction: string;
|
||||||
maxLen: number;
|
maxLen: number;
|
||||||
@ -15,5 +15,3 @@ interface LookupCardConfig {
|
|||||||
helpText: string;
|
helpText: string;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type {CardConfig, LookupCardConfig}
|
|
@ -1,14 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import LookupCard from "./LookupCard.svelte";
|
|
||||||
|
|
||||||
const LookupCardConfig = {
|
|
||||||
title: "Find By Headcode",
|
|
||||||
helpText: "",
|
|
||||||
formAction: "/train",
|
|
||||||
placeholder: "enter headcode",
|
|
||||||
maxLen: 4,
|
|
||||||
fieldName: "headcode",
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<LookupCard config={LookupCardConfig} />
|
|
@ -96,6 +96,10 @@
|
|||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.transport-mode {
|
||||||
|
padding-top: 20px;
|
||||||
|
height: 17px;
|
||||||
|
}
|
||||||
.table-head-text {
|
.table-head-text {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,10 @@
|
|||||||
|
|
||||||
<div class="headerBar">
|
<div class="headerBar">
|
||||||
<a href="/">
|
<a href="/">
|
||||||
<img src="/images/logo/wide_logo.svg" alt="OwlBoard Logo" />
|
<picture>
|
||||||
|
<source srcset="/images/logo/wide_logo.svg" type="image/svg+xml" />
|
||||||
|
<img src="/images/logo/wide_logo_200.png" alt="OwlBoard Logo" />
|
||||||
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
<header>{title}</header>
|
<header>{title}</header>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { setTelemetryFalse, setTelemetryTrue } from "$lib/stores/SetTelemetryConsent";
|
|
||||||
import { telemetry } from "$lib/stores/telemetryConsent";
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
import { browser } from "$app/environment";
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (!localStorage.getItem("telemetryRequested")) {
|
|
||||||
document.querySelector<HTMLDialogElement>("#analytics-consent")?.showModal();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Setting Function Calls
|
|
||||||
function setAcceptAnalytics() {
|
|
||||||
setTelemetryTrue();
|
|
||||||
localStorage.setItem("telemetryRequested", "yes");
|
|
||||||
document.querySelector<HTMLDialogElement>("#analytics-consent")?.close();
|
|
||||||
}
|
|
||||||
function setDenyAnalytics() {
|
|
||||||
setTelemetryFalse();
|
|
||||||
localStorage.setItem("telemetryRequested", "yes");
|
|
||||||
document.querySelector<HTMLDialogElement>("#analytics-consent")?.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reactively call telemetry script functions
|
|
||||||
$: {
|
|
||||||
if (browser) {
|
|
||||||
if ($telemetry) {
|
|
||||||
loadTelemetryScript();
|
|
||||||
} else {
|
|
||||||
removeTelemetryScript();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadTelemetryScript() {
|
|
||||||
console.log("Activating telemetry script")
|
|
||||||
if (browser) {
|
|
||||||
if (document.querySelector("script[data-entity='owlboard-frontend']")) return; // Prevent duplicate loading
|
|
||||||
|
|
||||||
const script = document.createElement("script");
|
|
||||||
script.type = "module";
|
|
||||||
script.dataset.entity = "owlboard-frontend";
|
|
||||||
script.src = "https://liwan.fjla.uk/script.js";
|
|
||||||
document.body.appendChild(script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeTelemetryScript() {
|
|
||||||
console.log("Deactivating telemetry script")
|
|
||||||
if (browser) {
|
|
||||||
document.querySelector("script[data-entity='owlboard-frontend']")?.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<dialog id="analytics-consent">
|
|
||||||
<h1>Telemetry</h1>
|
|
||||||
<p>
|
|
||||||
OwlBoard collects <strong>anonymous</strong> usage data, such as the most visited pages. Any personal data is anonymized to ensure it cannot be linked to individuals.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
This data is used to focus efforts, improving the most used features.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
By selecting Accept, you are helping to steer OwlBoard's future - if
|
|
||||||
you change your mind, head over to Settings.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nobody can be identified using any data stored, all data is available for
|
|
||||||
all to see <a href="https://liwan.fjla.uk" target="_blank">here</a>.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Further information can be found in the <a href="/more/privacy">Privacy Policy</a>.
|
|
||||||
</p>
|
|
||||||
<button class="modal-button" type="button" on:click={setAcceptAnalytics}>Accept</button>
|
|
||||||
<button class="modal-button" id="deny-button" type="button" on:click={setDenyAnalytics}>Deny</button>
|
|
||||||
</dialog>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
::backdrop {
|
|
||||||
background-color: var(--main-bg-color);
|
|
||||||
opacity: 75%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#analytics-consent {
|
|
||||||
width: 75vw;
|
|
||||||
max-width: 700px;
|
|
||||||
border-radius: 25px;
|
|
||||||
background-color: var(--island-bg-color);
|
|
||||||
opacity: 100;
|
|
||||||
color: var(--island-text-color);
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-button {
|
|
||||||
width: 25%;
|
|
||||||
min-width: 120px;
|
|
||||||
height: 40px;
|
|
||||||
font-size: larger;
|
|
||||||
margin: 5px;
|
|
||||||
border-radius: 30px;
|
|
||||||
background-color: var(--second-bg-color);
|
|
||||||
color: var(--island-text-color);
|
|
||||||
box-shadow: var(--box-shadow);
|
|
||||||
border: none;
|
|
||||||
font-family: urwgothic, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#deny-button {
|
|
||||||
background-color: var(--main-bg-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,14 +0,0 @@
|
|||||||
import { get } from "svelte/store";
|
|
||||||
import { telemetry } from "./telemetryConsent";
|
|
||||||
|
|
||||||
export function setTelemetryTrue() {
|
|
||||||
telemetry.set(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setTelemetryFalse() {
|
|
||||||
telemetry.set(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getTelemetry(): boolean {
|
|
||||||
return get(telemetry);
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
// src/stores.js
|
|
||||||
import { writable, type Writable } from "svelte/store";
|
|
||||||
import { browser } from "$app/environment";
|
|
||||||
|
|
||||||
// Initialize the store with a boolean value from local storage
|
|
||||||
export const telemetry: Writable<boolean> = writable(fromLocalStorage("telemetry", false));
|
|
||||||
toLocalStorage(telemetry, "telemetry");
|
|
||||||
|
|
||||||
function fromLocalStorage(storageKey: string, fallback: boolean): boolean {
|
|
||||||
if (browser) {
|
|
||||||
const storedValue = localStorage.getItem(storageKey);
|
|
||||||
if (storedValue !== null && storedValue !== "undefined") {
|
|
||||||
return storedValue === "true";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toLocalStorage(store: Writable<boolean>, storageKey: string) {
|
|
||||||
if (browser) {
|
|
||||||
store.subscribe((value: boolean) => {
|
|
||||||
localStorage.setItem(storageKey, String(value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +1,2 @@
|
|||||||
export const version: string = "2025.05.1";
|
export const version: string = "2024.07.4";
|
||||||
export const versionTag: string = "";
|
export const versionTag: string = "";
|
||||||
|
@ -10,12 +10,11 @@
|
|||||||
import TrainIcons from "./trainIcons.svelte";
|
import TrainIcons from "./trainIcons.svelte";
|
||||||
|
|
||||||
export let service: OB_TrainTT_service;
|
export let service: OB_TrainTT_service;
|
||||||
export let date: Date;
|
|
||||||
|
|
||||||
let isExpanded = false;
|
let isExpanded = false;
|
||||||
|
|
||||||
async function getTrainByUID(tuid = "") {
|
async function getTrainByUID(tuid = "") {
|
||||||
const url = `${getApiUrl()}/api/v2/timetable/train/${date.toISOString().split('T')[0]}/byTrainUid/${tuid}`;
|
const url = `${getApiUrl()}/api/v2/timetable/train/now/byTrainUid/${tuid}`;
|
||||||
const options = {
|
const options = {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -5,16 +5,12 @@
|
|||||||
const title = "OwlBoard - Error";
|
const title = "OwlBoard - Error";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
|
|
||||||
<h1>{$page.status}: {$page?.error?.message}</h1>
|
<h1>{$page.status}: {$page?.error?.message}</h1>
|
||||||
|
|
||||||
{#if $page.status === 404}
|
{#if $page.status === 404}
|
||||||
<p>There's no light at the end of this tunnel</p>
|
<p>This is not the page you're looking for.</p>
|
||||||
<p>The page you are looking for doesn't exist, use the tabs below to find another page.</p>
|
<p>The page you are looking for doesn't exist, use the tabs below to find another page.</p>
|
||||||
{:else if $page.status === 500}
|
{:else if $page.status === 500}
|
||||||
<p>
|
<p>
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
import DevBanner from "$lib/DevBanner.svelte";
|
import DevBanner from "$lib/DevBanner.svelte";
|
||||||
import { page } from "$app/stores";
|
import { page } from "$app/stores";
|
||||||
import { Toaster } from "svelte-french-toast";
|
import { Toaster } from "svelte-french-toast";
|
||||||
import AnalyticsConsent from "$lib/popover/analytics-consent.svelte";
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@ -22,7 +21,7 @@
|
|||||||
<meta name="author" content="Frederick Boniface" />
|
<meta name="author" content="Frederick Boniface" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="Get instant access to live train data, PIS codes, and location reference codes. Built by railway staff, for railway staff - your fastest route to accurate information."
|
content="Get instant access to live train data, PIS codes, and location reference codes. Built by railway staff, for railway staff – your fastest route to accurate information."
|
||||||
/>
|
/>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta name="theme-color" content="#00b7b7" />
|
<meta name="theme-color" content="#00b7b7" />
|
||||||
@ -34,7 +33,6 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<AnalyticsConsent />
|
|
||||||
|
|
||||||
{#if dev}
|
{#if dev}
|
||||||
<DevBanner />
|
<DevBanner />
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
import LookupCard from "$lib/cards/LookupCard.svelte";
|
import LookupCard from "$lib/cards/LookupCard.svelte";
|
||||||
import NearToMeCard from "$lib/cards/NearToMeCard.svelte";
|
import NearToMeCard from "$lib/cards/NearToMeCard.svelte";
|
||||||
import QuickLinkCard from "$lib/cards/QuickLinkCard.svelte";
|
import QuickLinkCard from "$lib/cards/QuickLinkCard.svelte";
|
||||||
import FindByHeadcodeCard from "$lib/cards/FindByHeadcodeCard.svelte";
|
|
||||||
const title = "OwlBoard";
|
const title = "OwlBoard";
|
||||||
const lookupCards: LookupCardConfig[] = [
|
const lookupCards: LookupCardConfig[] = [
|
||||||
{
|
{
|
||||||
@ -18,15 +17,23 @@
|
|||||||
placeholder: "enter crs/tiploc",
|
placeholder: "enter crs/tiploc",
|
||||||
maxLen: 7,
|
maxLen: 7,
|
||||||
fieldName: "station",
|
fieldName: "station",
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
title: "Timetable & PIS",
|
||||||
|
helpText: "",
|
||||||
|
formAction: "/train",
|
||||||
|
placeholder: "enter headcode",
|
||||||
|
maxLen: 4,
|
||||||
|
fieldName: "headcode",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const featureSupport = featureDetect();
|
const featureSupport = featureDetect();
|
||||||
if (!featureSupport.critical) {
|
if (!featureSupport.critical) {
|
||||||
toast.error("Use a newer browser or OwlBoard might not work properly. See `Menu > Statistics` for more information.");
|
toast.error("Your browser is missing critical features, OwlBoard might not work properly. See `Menu > Statistics` for more information.");
|
||||||
} else if (!featureSupport.nice) {
|
} else if (!featureSupport.nice) {
|
||||||
toast.error("Use a newer browser for the best experience, see `Menu > Statistics` for more information.");
|
toast.error("Your browser is missing some features, see `Menu > Statistics` for more information.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -35,7 +42,6 @@
|
|||||||
{#each lookupCards as config}
|
{#each lookupCards as config}
|
||||||
<LookupCard {config} />
|
<LookupCard {config} />
|
||||||
{/each}
|
{/each}
|
||||||
<FindByHeadcodeCard />
|
|
||||||
<NearToMeCard />
|
<NearToMeCard />
|
||||||
<QuickLinkCard />
|
<QuickLinkCard />
|
||||||
|
|
||||||
|
@ -5,10 +5,6 @@
|
|||||||
const title = "404 - Not Found";
|
const title = "404 - Not Found";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
<h1 class="heading">There's no light at the end of this tunnel</h1>
|
<h1 class="heading">There's no light at the end of this tunnel</h1>
|
||||||
<p>The page you were looking for wasn't found</p>
|
<p>The page you were looking for wasn't found</p>
|
||||||
|
@ -5,10 +5,6 @@
|
|||||||
const title = "50x - Server Error";
|
const title = "50x - Server Error";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
<h1 class="heading">This page has been delayed by more servers than usual needing repairs at the same time</h1>
|
<h1 class="heading">This page has been delayed by more servers than usual needing repairs at the same time</h1>
|
||||||
<p>There was an error with the server, please try again later</p>
|
<p>There was an error with the server, please try again later</p>
|
||||||
|
@ -32,10 +32,6 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
|
|
||||||
{#if !blockLoading}
|
{#if !blockLoading}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
<script>
|
|
||||||
import LargeLogo from "$lib/images/large-logo.svelte";
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
<LargeLogo />
|
|
||||||
<h1>
|
|
||||||
OwlBoard is down for maintenance
|
|
||||||
</h1>
|
|
||||||
<p>
|
|
||||||
Maintenance is expected to be complete by 23:59 on 22/11/2024
|
|
||||||
</p>
|
|
@ -2,6 +2,7 @@
|
|||||||
import Header from "$lib/navigation/header.svelte";
|
import Header from "$lib/navigation/header.svelte";
|
||||||
import Nav from "$lib/navigation/nav.svelte";
|
import Nav from "$lib/navigation/nav.svelte";
|
||||||
import {
|
import {
|
||||||
|
IconCode,
|
||||||
IconHelp,
|
IconHelp,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
IconLocation,
|
IconLocation,
|
||||||
@ -16,11 +17,11 @@
|
|||||||
const title = "More";
|
const title = "More";
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
{ title: "Help", path: "https://docs.owlboard.info", icon: IconHelp },
|
|
||||||
{ title: "Settings", path: "/more/settings", icon: IconSettings },
|
|
||||||
{ title: "Your Data", path: "/more/data", icon: IconUser },
|
{ title: "Your Data", path: "/more/data", icon: IconUser },
|
||||||
{ title: "About", path: "/more/about", icon: IconInfoCircle },
|
|
||||||
{ title: "Registration", path: "/more/reg", icon: IconUserPlus },
|
{ title: "Registration", path: "/more/reg", icon: IconUserPlus },
|
||||||
|
{ title: "Settings", path: "/more/settings", icon: IconSettings },
|
||||||
|
{ title: "Help", path: "/more/help", icon: IconHelp },
|
||||||
|
{ title: "About", path: "/more/about", icon: IconInfoCircle },
|
||||||
{ title: "Location Reference Code Lookup", path: "/more/corpus", icon: IconLocation },
|
{ title: "Location Reference Code Lookup", path: "/more/corpus", icon: IconLocation },
|
||||||
{ title: "Reason Code Lookup", path: "/more/reasons", icon: IconMessageCode },
|
{ title: "Reason Code Lookup", path: "/more/reasons", icon: IconMessageCode },
|
||||||
{ title: "Privacy Policy", path: "/more/privacy", icon: IconSpy },
|
{ title: "Privacy Policy", path: "/more/privacy", icon: IconSpy },
|
||||||
@ -29,10 +30,6 @@
|
|||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
|
|
||||||
{#each links as item}
|
{#each links as item}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
<LargeLogo />
|
<LargeLogo />
|
||||||
<p class="neg">© 2022-2025 Frederick Boniface</p>
|
<p class="neg">© 2022-2023 Frederick Boniface</p>
|
||||||
<p>OwlBoard was created by train crew for train crew.</p>
|
<p>OwlBoard was created by train crew for train crew.</p>
|
||||||
<p>I developed OwlBoard in 2022 with the aim of providing fast and easy access to the information we need on a daily basis.</p>
|
<p>I developed OwlBoard in 2022 with the aim of providing fast and easy access to the information we need on a daily basis.</p>
|
||||||
<p>Data is sourced from National Rail Enquiries, the OwlBoard Project, and Network Rail.</p>
|
<p>Data is sourced from National Rail Enquiries, the OwlBoard Project, and Network Rail.</p>
|
||||||
|
@ -35,10 +35,6 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
|
|
||||||
<p>OwlBoard stores as little data about you as possible to offer the service.</p>
|
<p>OwlBoard stores as little data about you as possible to offer the service.</p>
|
||||||
|
@ -3,10 +3,6 @@
|
|||||||
import Nav from "$lib/navigation/nav.svelte";
|
import Nav from "$lib/navigation/nav.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header title={"Help"} />
|
<Header title={"Help"} />
|
||||||
<Nav />
|
<Nav />
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
@ -4,72 +4,41 @@
|
|||||||
const title = "Privacy Policy";
|
const title = "Privacy Policy";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
<div>
|
<div>
|
||||||
<h2>Your Data</h2>
|
<p>
|
||||||
<p>
|
OwlBoard stores the minimum amount of data necessary to provide its functions for your use. No personal data is stored unless you report an issue. To review the specific
|
||||||
OwlBoard logs the IP addresses of its users, this is done on the basis of legitimate
|
data that we store, please visit <a href="/more/data">My Data</a>.
|
||||||
interest to ensure the security of the platform and to protect all users from
|
</p>
|
||||||
malicious activity. See <a href="#datasharing">Data Sharing</a> for details on how
|
<p>OwlBoard does not utilize any cookies. IP addresses and browser fingerprints are not logged.</p>
|
||||||
we may share this data.
|
<h2>If You Do Not Sign Up</h2>
|
||||||
</p>
|
<p>
|
||||||
<p>
|
If you choose not to sign up, no personal data will be processed or stored unless you report an issue. Any personal settings are stored locally in your browser and do not
|
||||||
With the exception of sending emails, all data is held - and always remains within -
|
leave your device.
|
||||||
the United Kingdom.
|
</p>
|
||||||
</p>
|
<h2>If You Sign Up</h2>
|
||||||
<h3>Telemetry</h3>
|
<p>
|
||||||
<p>
|
If you sign up for the rail staff version of OwlBoard, we do require the storage of some data. However, none of this data can be used to personally identify you. Any
|
||||||
If you opt-in to Telemetry, you will share your IP address and information about the
|
personal settings are stored locally in your browser and do not leave your device.
|
||||||
type of device and software you're using to access the service. This data is
|
</p>
|
||||||
anonymised and cannot be traced back to any individual. You can opt-in or opt-out in
|
<p>
|
||||||
your <a href="/more/settings">Settings</a>. All of the anonymised data can be viewed
|
During the sign-up process, you will be asked to provide a work email address, which will be checked to confirm its origin from a railway company. Once confirmed, an email
|
||||||
at: <a target="_blank" href="https://liwan.fjla.uk">liwan.fjla.uk</a> at any time.
|
containing a registration link will be sent to you. At this point, the username portion of your email address is discarded. For example, if your email address is
|
||||||
</p>
|
'a-user@owlboard.info', only 'owlboard.info' will be stored. This host portion of your email address is stored to filter and display relevant results prominently.
|
||||||
<p>
|
</p>
|
||||||
All of the data that is stored is held within the United Kingdom.
|
<p>The email server may store the address and message content as part of its regular operation, and your consent to this is implied when you sign up.</p>
|
||||||
</p>
|
<p>In addition to the host portion of your email address, a randomly generated UUID is stored for the purpose of authorizing access to the rail staff data.</p>
|
||||||
<p>
|
<p>
|
||||||
Telemetry data is used to identify which areas of the webapp are used most frequently
|
If you enable location data, your location will be sent to the server when you navigate to the homepage to determine your closest stations. This data is never stored on the
|
||||||
and where improvements need to be made.
|
server after the nearest stations have been send to your device.
|
||||||
</p>
|
</p>
|
||||||
<h3>Registering</h3>
|
<h2>Reporting an Issue</h2>
|
||||||
<p>
|
<p>When you report an issue, certain data is collected, including your browser's User Agent string and the size of the window in which you are viewing the website.</p>
|
||||||
If you register, your email address will be used to verify that you work for an
|
<p>
|
||||||
organisation that is permitted to access staff data on the service. An activation
|
Any data submitted when reporting an issue will be publicly viewable alongside the <a href="https://git.fjla.uk/owlboard/backend/issues" target="_blank"
|
||||||
email will be sent before your email address is anonymised. For example, if your
|
>OwlBoard/backend git repository</a
|
||||||
email address is hello@owlboard.info, it will be anonymized to @owlboard.info.
|
>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
OwlBoard emails are sent using Proton Mail, a privacy-first email service based in
|
|
||||||
Switzerland. To facilitate this, your email address will be securely sent to
|
|
||||||
Proton Mail securely.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
You will be assigned a unique identifier which will be stored alongside your
|
|
||||||
anonymised email address as well as in your browser. This is how the service
|
|
||||||
verifies that you are authorised to access staff data.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2 id="datasharing">Data Sharing</h2>
|
|
||||||
<p>
|
|
||||||
OwlBoard utilises CrowdSec, a security service that helps to protect the platform from
|
|
||||||
malicious activity. As part of its operation, CrowdSec analyzes IP addresses and may
|
|
||||||
share certain information with its community of users to identify and mitigate
|
|
||||||
security threats. If your IP address is identified as part of a security incident or
|
|
||||||
threat, it may be shared with CrowdSec's network for further analysis and to prevent
|
|
||||||
malicious activity. This sharing of IP addresses is done under the legitimate
|
|
||||||
interest basis for ensuring the security of the platform and protecting all users
|
|
||||||
from malicious activity.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
CrowdSec anonymizes and processes data in accordance with its own privacy policy, which
|
|
||||||
is available for review here. We recommend reviewing their policy to understand how
|
|
||||||
they handle any data collected.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Nav />
|
<Nav />
|
||||||
@ -81,23 +50,12 @@
|
|||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
color: var(--second-text-color);
|
color: var(--second-text-color);
|
||||||
margin: auto;
|
margin: 10px;
|
||||||
width: 90vw;
|
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
|
||||||
color: var(--second-text-color);
|
|
||||||
margin: auto;
|
|
||||||
width: 90vw;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
p {
|
p {
|
||||||
color: white;
|
color: white;
|
||||||
margin: auto;
|
margin: 10px;
|
||||||
padding-top: 5px;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
width: 90vw;
|
|
||||||
max-width: 550px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
import Nav from "$lib/navigation/nav.svelte";
|
import Nav from "$lib/navigation/nav.svelte";
|
||||||
import Loading from "$lib/navigation/loading.svelte";
|
import Loading from "$lib/navigation/loading.svelte";
|
||||||
import Card from "$lib/cards/Card.svelte";
|
import Card from "$lib/cards/Card.svelte";
|
||||||
import type { CardConfig } from "$lib/cards/Card.types";
|
import { CardConfig } from "$lib/cards/Card.types";
|
||||||
import { apiGet } from "$lib/scripts/apiFetch";
|
import { apiGet } from "$lib/scripts/apiFetch";
|
||||||
|
|
||||||
interface ApiResponse {
|
interface ApiResponse {
|
||||||
|
@ -43,9 +43,9 @@
|
|||||||
|
|
||||||
function send() {
|
function send() {
|
||||||
toast.promise(request(), {
|
toast.promise(request(), {
|
||||||
loading: "Sending email...",
|
loading: "Contacting Server...",
|
||||||
success: "Sent, check your inbox",
|
success: "Request Answered.",
|
||||||
error: "Error sending email",
|
error: "Unable to contact server.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,11 +7,37 @@
|
|||||||
const title = "Submit Registration";
|
const title = "Submit Registration";
|
||||||
let state = false;
|
let state = false;
|
||||||
let status: string;
|
let status: string;
|
||||||
let inputString: string;
|
|
||||||
|
let inputs: { id: string; value: string }[] = [
|
||||||
|
{ id: "1", value: "" },
|
||||||
|
{ id: "2", value: "" },
|
||||||
|
{ id: "3", value: "" },
|
||||||
|
{ id: "4", value: "" },
|
||||||
|
{ id: "5", value: "" },
|
||||||
|
{ id: "6", value: "" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function handleInput(index: number, event: KeyboardEvent): void {
|
||||||
|
if (event.key === "Backspace" && index > 0 && inputs[index].value === "") {
|
||||||
|
const prevInput = document.getElementById(`input-${index}`);
|
||||||
|
if (prevInput) {
|
||||||
|
prevInput.focus();
|
||||||
|
}
|
||||||
|
} else if (inputs[index].value.length === 1) {
|
||||||
|
const nextInput = document.getElementById(`input-${index + 2}`);
|
||||||
|
if (nextInput) {
|
||||||
|
nextInput.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
console.log(`Code: ${inputString}`);
|
let submitString: string = "";
|
||||||
const res = await submit(inputString);
|
for (const input of inputs) {
|
||||||
|
submitString += input.value.toUpperCase();
|
||||||
|
}
|
||||||
|
console.log(`Code: ${submitString}`);
|
||||||
|
const res = await submit(submitString);
|
||||||
console.log(`Registration Status: ${res}`);
|
console.log(`Registration Status: ${res}`);
|
||||||
if (res == 201) {
|
if (res == 201) {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
@ -45,10 +71,6 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
|
|
||||||
{#if state}
|
{#if state}
|
||||||
@ -63,7 +85,9 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<p class="title-ish">Enter your registration code below</p>
|
<p class="title-ish">Enter your registration code below</p>
|
||||||
<form on:submit={handleSubmit} id="codeInputForm">
|
<form on:submit={handleSubmit} id="codeInputForm">
|
||||||
<input class="code-in" bind:value={inputString} id={'input'} maxlength="6" autocomplete="off">
|
{#each inputs as input, index}
|
||||||
|
<input class="code-in" bind:value={input.value} id={`input-${input.id}`} maxlength="1" autocomplete="off" on:keydown={(event) => handleInput(index, event)} />
|
||||||
|
{/each}
|
||||||
<br />
|
<br />
|
||||||
<button type="submit">Submit</button>
|
<button type="submit">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
@ -78,7 +102,7 @@
|
|||||||
.code-in {
|
.code-in {
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
width: calc(29px * 6);
|
width: 29px;
|
||||||
height: 39px;
|
height: 39px;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -76,10 +76,6 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
|
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
import QlSet from "$lib/islands/quick-link-set-island.svelte";
|
import QlSet from "$lib/islands/quick-link-set-island.svelte";
|
||||||
import Island from "$lib/islands/island.svelte";
|
import Island from "$lib/islands/island.svelte";
|
||||||
import { location } from "$lib/stores/location";
|
import { location } from "$lib/stores/location";
|
||||||
import { telemetry } from "$lib/stores/telemetryConsent";
|
|
||||||
import { getCurrentLocation } from "$lib/scripts/getLocation";
|
import { getCurrentLocation } from "$lib/scripts/getLocation";
|
||||||
import toast from "svelte-french-toast";
|
import toast from "svelte-french-toast";
|
||||||
const title = "Settings";
|
const title = "Settings";
|
||||||
@ -13,15 +12,11 @@
|
|||||||
getCurrentLocation();
|
getCurrentLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmationToast() {
|
function locationToast() {
|
||||||
toast.success("Settings updated");
|
toast("Settings updated");
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
|
|
||||||
<QlSet />
|
<QlSet />
|
||||||
@ -30,19 +25,10 @@
|
|||||||
<p>Use your location to quickly check departure boards near you</p>
|
<p>Use your location to quickly check departure boards near you</p>
|
||||||
<div class="checkbox-container">
|
<div class="checkbox-container">
|
||||||
<label for="location_enable">Enabled</label>
|
<label for="location_enable">Enabled</label>
|
||||||
<input id="location_enable" type="checkbox" bind:checked={$location} on:click={confirmationToast} />
|
<input id="location_enable" type="checkbox" bind:checked={$location} on:click={locationToast} />
|
||||||
</div>
|
</div>
|
||||||
</Island>
|
</Island>
|
||||||
|
|
||||||
<Island variables={{title:"Telemetry"}}>
|
|
||||||
<p>Telemetry helps shape the future of OwlBoard - all data is anonymised. To find out more, see the
|
|
||||||
<a href="/more/privacy">privacy policy</a>.
|
|
||||||
</p>
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<label for="telemetry_enable">Enabled</label>
|
|
||||||
<input id="telemetry_enable" type="checkbox" bind:checked={$telemetry} on:click={confirmationToast} />
|
|
||||||
</Island>
|
|
||||||
|
|
||||||
<Nav />
|
<Nav />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -61,5 +47,4 @@
|
|||||||
margin-right: 25px;
|
margin-right: 25px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -43,10 +43,6 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
|
|
||||||
{#await getData()}
|
{#await getData()}
|
||||||
|
@ -17,10 +17,6 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
|
|
||||||
<LargeLogo />
|
<LargeLogo />
|
||||||
|
@ -1,34 +1,39 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Header from "$lib/navigation/header.svelte";
|
import Header from "$lib/navigation/header.svelte";
|
||||||
import Nav from "$lib/navigation/nav.svelte";
|
import Nav from "$lib/navigation/nav.svelte";
|
||||||
|
import Island from "$lib/islands/island.svelte";
|
||||||
|
import Loading from "$lib/navigation/loading.svelte";
|
||||||
import { uuid } from "$lib/stores/uuid";
|
import { uuid } from "$lib/stores/uuid";
|
||||||
import StylesToc from "$lib/train/styles-toc.svelte";
|
import StylesToc from "$lib/train/styles-toc.svelte";
|
||||||
import { getApiUrl } from "$lib/scripts/upstream";
|
import { getApiUrl } from "$lib/scripts/upstream";
|
||||||
import toast from "svelte-french-toast";
|
import toast from "svelte-french-toast";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import type { OB_Pis_FullObject } from "@owlboard/ts-types";
|
|
||||||
import Card from "$lib/cards/Card.svelte";
|
|
||||||
import type { CardConfig } from "$lib/cards/Card.types";
|
|
||||||
import FindByHeadcodeCard from "$lib/cards/FindByHeadcodeCard.svelte";
|
|
||||||
|
|
||||||
const title = "PIS Finder";
|
const title = "PIS Finder";
|
||||||
|
const variables = { title: "Results" };
|
||||||
let entryPIS = "";
|
let entryPIS = "";
|
||||||
let entryStartCRS = "";
|
let entryStartCRS = "";
|
||||||
let entryEndCRS = "";
|
let entryEndCRS = "";
|
||||||
let data: OB_Pis_FullObject[] = [];
|
let data = [];
|
||||||
let error = false;
|
let error = false;
|
||||||
let errMsg = "Unknown Error";
|
let errMsg = "Unknown Error";
|
||||||
|
let isLoading = false;
|
||||||
|
|
||||||
async function findByStartEnd() {
|
async function findByStartEnd() {
|
||||||
|
isLoading = true;
|
||||||
const url = `${getApiUrl()}/api/v2/pis/byStartEnd/${entryStartCRS}/${entryEndCRS}`;
|
const url = `${getApiUrl()}/api/v2/pis/byStartEnd/${entryStartCRS}/${entryEndCRS}`;
|
||||||
await fetchData(url);
|
await fetchData(url);
|
||||||
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Temporarily Disabled
|
||||||
async function findByPis() {
|
async function findByPis() {
|
||||||
|
isLoading = true;
|
||||||
const url = `${getApiUrl()}/api/v2/pis/byCode/${entryPIS}`;
|
const url = `${getApiUrl()}/api/v2/pis/byCode/${entryPIS}`;
|
||||||
await fetchData(url);
|
await fetchData(url);
|
||||||
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
async function fetchData(url: string) {
|
async function fetchData(url: string) {
|
||||||
const options = {
|
const options = {
|
||||||
@ -71,58 +76,29 @@
|
|||||||
entryPIS = "";
|
entryPIS = "";
|
||||||
entryStartCRS = "";
|
entryStartCRS = "";
|
||||||
entryEndCRS = "";
|
entryEndCRS = "";
|
||||||
|
toast.success("Form reset");
|
||||||
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if ($uuid == null || $uuid == "") {
|
toast("Registration soon required for PIS features.\n\nClick 'Register' in the menu.", {
|
||||||
toast("You must register to see results", {
|
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const resultsCard: CardConfig = {
|
|
||||||
title: "Results",
|
|
||||||
showHelp: false,
|
|
||||||
helpText: "",
|
|
||||||
showRefresh: false,
|
|
||||||
onRefresh: ()=>{},
|
|
||||||
refreshing: false
|
|
||||||
}
|
|
||||||
const errorCard: CardConfig = {
|
|
||||||
title: "Error",
|
|
||||||
showHelp: true,
|
|
||||||
helpText: "There was an error searching for PIS Codes",
|
|
||||||
showRefresh: false,
|
|
||||||
onRefresh: () => {},
|
|
||||||
refreshing: false,
|
|
||||||
}
|
|
||||||
const findByStartEndCard: CardConfig = {
|
|
||||||
title: "Find by Start/End CRS",
|
|
||||||
showHelp: true,
|
|
||||||
helpText: "Enter a start and end CRS Code",
|
|
||||||
showRefresh: false,
|
|
||||||
onRefresh: () => {},
|
|
||||||
refreshing: false,
|
|
||||||
}
|
|
||||||
const findByPisCodeCard: CardConfig = {
|
|
||||||
title: "Find by PIS Code",
|
|
||||||
showHelp: true,
|
|
||||||
helpText: "Enter a PIS Code to see its details. (Four digits)",
|
|
||||||
showRefresh: false,
|
|
||||||
onRefresh: () => {},
|
|
||||||
refreshing: false,
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
|
|
||||||
|
{#if isLoading}
|
||||||
|
<Loading />
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if error}
|
{#if error}
|
||||||
<Card config={errorCard}>
|
<Island {variables}>
|
||||||
<p class="error">{errMsg}</p>
|
<p class="error">{errMsg}</p>
|
||||||
</Card>
|
</Island>
|
||||||
{:else if data.length}
|
{:else if data.length}
|
||||||
<Card config={resultsCard}>
|
<Island {variables}>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="toc">TOC</th>
|
<th class="toc">TOC</th>
|
||||||
@ -137,28 +113,27 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</table>
|
</table>
|
||||||
</Card>
|
</Island>
|
||||||
<button id="reset" type="reset" on:click={reset}>Reset</button>
|
|
||||||
{:else}
|
{:else}
|
||||||
<FindByHeadcodeCard />
|
<p>To search by headcode use the Train Finder on the homepage</p>
|
||||||
<Card config={findByStartEndCard}>
|
<p>This feature now supports all GWR Services</p>
|
||||||
|
<p class="label">Find By Start/End CRS:</p>
|
||||||
<form on:submit={findByStartEnd}>
|
<form on:submit={findByStartEnd}>
|
||||||
<input type="text" maxlength="3" pattern="^[A-Za-z]+$" autocomplete="off" placeholder="Start" required bind:value={entryStartCRS} />
|
<input type="text" maxlength="3" autocomplete="off" placeholder="Start" bind:value={entryStartCRS} />
|
||||||
<input type="text" maxlength="3" pattern="^[A-Za-z]+$" autocomplete="off" placeholder="End" required bind:value={entryEndCRS} />
|
<input type="text" maxlength="3" autocomplete="off" placeholder="End" bind:value={entryEndCRS} />
|
||||||
<br />
|
<br />
|
||||||
<button type="submit">Search</button>
|
<button type="submit">Search</button>
|
||||||
<button type="reset">Clear</button>
|
|
||||||
</form>
|
</form>
|
||||||
</Card>
|
<!-- FIND BY PIS CODE NOT WORKING AT PRESENT
|
||||||
<Card config={findByPisCodeCard}>
|
<p class="label">Find By PIS Code:</p>
|
||||||
<form on:submit={findByPis}>
|
<form on:submit={findByPis}>
|
||||||
<input type="text" maxlength="4" pattern="^\d+$" autocomplete="off" placeholder="PIS" required bind:value={entryPIS} />
|
<input type="number" max="9999" autocomplete="off" placeholder="PIS" bind:value={entryPIS} />
|
||||||
<br />
|
<br />
|
||||||
<button type="submit">Search</button>
|
<button type="submit">Search</button>
|
||||||
<button type="reset" >Clear</button>
|
</form>
|
||||||
</form>
|
-->
|
||||||
</Card>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
<button id="reset" type="reset" on:click={reset}>Reset</button>
|
||||||
<Nav />
|
<Nav />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -166,20 +141,21 @@
|
|||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
.label {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--main-text-color);
|
||||||
|
}
|
||||||
input {
|
input {
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
font-family: urwgothic, sans-serif;
|
font-family: urwgothic, sans-serif;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
width: 25%;
|
width: 30%;
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
margin-left: 10px;
|
|
||||||
margin-right: 10px;
|
|
||||||
box-shadow: var(--box-shadow);
|
box-shadow: var(--box-shadow);
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
@ -191,9 +167,8 @@
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
margin-bottom: 15px;
|
|
||||||
height: 30px;
|
height: 30px;
|
||||||
background-color: var(--island-button-color);
|
background-color: var(--island-bg-color);
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
box-shadow: var(--box-shadow);
|
box-shadow: var(--box-shadow);
|
||||||
|
39
src/routes/test/+page.svelte
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { LookupCardConfig } from "$lib/cards/Card.types";
|
||||||
|
import LookupCard from "$lib/cards/LookupCard.svelte";
|
||||||
|
import NearToMeCard from "$lib/cards/NearToMeCard.svelte";
|
||||||
|
import QuickLinkCard from "$lib/cards/QuickLinkCard.svelte";
|
||||||
|
import Header from "$lib/navigation/header.svelte";
|
||||||
|
import Nav from "$lib/navigation/nav.svelte";
|
||||||
|
import TimeBar from "$lib/navigation/TimeBar.svelte";
|
||||||
|
|
||||||
|
let LookupConfig: LookupCardConfig = {
|
||||||
|
title: "Live Arr/Dep Boards",
|
||||||
|
helpText: "Enter CRS, TIPLOC or STANOX code to see live departures",
|
||||||
|
placeholder: "Enter CRS/TIPLOC",
|
||||||
|
maxLen: 7,
|
||||||
|
formAction: "/ldb/",
|
||||||
|
fieldName: "station",
|
||||||
|
};
|
||||||
|
|
||||||
|
let TimetableConfig: LookupCardConfig = {
|
||||||
|
title: "Timetable & PIS",
|
||||||
|
helpText: "Enter a headcode to search the timetable and check PIS Codes",
|
||||||
|
placeholder: "Enter headcode",
|
||||||
|
maxLen: 4,
|
||||||
|
formAction: "/train/",
|
||||||
|
fieldName: "headcode",
|
||||||
|
};
|
||||||
|
|
||||||
|
function onRefresh() {
|
||||||
|
console.log("Refresh");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Header title={"Test"} />
|
||||||
|
<TimeBar updatedTime={new Date()} />
|
||||||
|
<LookupCard config={LookupConfig} />
|
||||||
|
<LookupCard config={TimetableConfig} />
|
||||||
|
<NearToMeCard />
|
||||||
|
<QuickLinkCard />
|
||||||
|
<Nav />
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Header from "$lib/navigation/header.svelte";
|
import Header from "$lib/navigation/header.svelte";
|
||||||
|
import Loading from "$lib/navigation/loading.svelte";
|
||||||
import Island from "$lib/islands/island.svelte";
|
import Island from "$lib/islands/island.svelte";
|
||||||
import Nav from "$lib/navigation/nav.svelte";
|
import Nav from "$lib/navigation/nav.svelte";
|
||||||
import { uuid } from "$lib/stores/uuid";
|
import { uuid } from "$lib/stores/uuid";
|
||||||
@ -9,17 +10,14 @@
|
|||||||
import { getApiUrl } from "$lib/scripts/upstream";
|
import { getApiUrl } from "$lib/scripts/upstream";
|
||||||
import toast from "svelte-french-toast";
|
import toast from "svelte-french-toast";
|
||||||
import TimeBar from "$lib/navigation/TimeBar.svelte";
|
import TimeBar from "$lib/navigation/TimeBar.svelte";
|
||||||
import { IconArrowLeft, IconArrowRight, IconCheck } from "@tabler/icons-svelte";
|
|
||||||
import Error from "../+error.svelte";
|
|
||||||
|
|
||||||
let title = "Timetable Results";
|
let title = "Timetable Results";
|
||||||
let id = "";
|
let id = "";
|
||||||
let data = [];
|
let data = [];
|
||||||
|
let isLoading = true;
|
||||||
let error = false;
|
let error = false;
|
||||||
let errMsg = "";
|
let errMsg = "";
|
||||||
|
|
||||||
let formattedDate = new Date().toISOString().split('T')[0];
|
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (id) {
|
if (id) {
|
||||||
title = id.toUpperCase();
|
title = id.toUpperCase();
|
||||||
@ -32,52 +30,26 @@
|
|||||||
return new URLSearchParams(window.location.search).get("headcode");
|
return new URLSearchParams(window.location.search).get("headcode");
|
||||||
}
|
}
|
||||||
|
|
||||||
function incrementDate() {
|
|
||||||
let dateInput = new Date(formattedDate)
|
|
||||||
dateInput.setDate(dateInput.getDate() + 1);
|
|
||||||
formattedDate = dateInput.toISOString().split('T')[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function decrementDate() {
|
|
||||||
let dateInput = new Date(formattedDate)
|
|
||||||
dateInput.setDate(dateInput.getDate() - 1);
|
|
||||||
formattedDate = dateInput.toISOString().split('T')[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
isLoading = true;
|
||||||
id = (await getHeadcode()) || "";
|
id = (await getHeadcode()) || "";
|
||||||
load();
|
const res = await fetchData(id);
|
||||||
|
if (res) {
|
||||||
|
data = res;
|
||||||
|
if (!data.length) {
|
||||||
|
error = true;
|
||||||
|
errMsg = "No services found";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isLoading = false;
|
||||||
|
|
||||||
if ($uuid == null || $uuid == "") {
|
toast("Registration soon required for timetable features.\n\nClick 'Register' in the menu.", {
|
||||||
toast("Register to see PIS codes", {
|
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function load() {
|
|
||||||
error = false;
|
|
||||||
const selectedDate = new Date(formattedDate);
|
|
||||||
const currentDate = new Date();
|
|
||||||
const difference: number = currentDate.getTime() - selectedDate.getTime();
|
|
||||||
const differenceDays: number = difference / (1000 * 60 * 60 * 24)
|
|
||||||
if (differenceDays > 7) {
|
|
||||||
toast.error("Timetable data is not available for dates older than a week")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.promise(
|
|
||||||
fetchData(id),
|
|
||||||
{
|
|
||||||
loading: 'Searching Timetable',
|
|
||||||
success: 'Done',
|
|
||||||
error: 'No Services Found'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchData(id = "") {
|
async function fetchData(id = "") {
|
||||||
data = [];
|
const date = "now";
|
||||||
const searchType = "headcode";
|
const searchType = "headcode";
|
||||||
const options = {
|
const options = {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@ -85,59 +57,44 @@
|
|||||||
uuid: $uuid,
|
uuid: $uuid,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const url = `${getApiUrl()}/api/v2/timetable/train/${formattedDate}/${searchType}/${id}`;
|
const url = `${getApiUrl()}/api/v2/timetable/train/${date}/${searchType}/${id}`;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(url, options);
|
const res = await fetch(url, options);
|
||||||
if (res.status < 300) {
|
if (res.status == 200) {
|
||||||
let services = await res.json();
|
return await res.json();
|
||||||
if (!services.length) {
|
|
||||||
error = true;
|
|
||||||
errMsg = "No services found";
|
|
||||||
return Promise.reject(new Error(errMsg));
|
|
||||||
}
|
|
||||||
data = services
|
|
||||||
} else if (res.status === 401) {
|
} else if (res.status === 401) {
|
||||||
error = true;
|
error = true;
|
||||||
errMsg = "You must be logged into the staff version for this feature";
|
errMsg = "You must be logged into the staff version for this feature";
|
||||||
return Promise.reject(new Error(errMsg));
|
return false;
|
||||||
} else {
|
} else {
|
||||||
error = true;
|
error = true;
|
||||||
errMsg = "Unable to connect, check your connection and try again";
|
errMsg = "Unable to connect, check your connection and try again";
|
||||||
return Promise.reject(new Error(errMsg));
|
return false;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = true;
|
error = true;
|
||||||
errMsg = "Connection error, try again later";
|
errMsg = "Connection error, try again later";
|
||||||
return Promise.reject(new Error(errMsg));
|
|
||||||
}
|
}
|
||||||
|
isLoading = false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header {title} />
|
<Header {title} />
|
||||||
<TimeBar updatedTime={undefined} />
|
<TimeBar updatedTime={undefined} />
|
||||||
<div id="dateSelector">
|
|
||||||
<button on:click={decrementDate}><IconArrowLeft /></button>
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
id="dateInput"
|
|
||||||
bind:value={formattedDate}
|
|
||||||
/>
|
|
||||||
<button on:click={incrementDate}><IconArrowRight /></button>
|
|
||||||
<button on:click={load}><IconCheck /></button>
|
|
||||||
</div>
|
|
||||||
{#if error}
|
{#if error}
|
||||||
<Island>
|
<Island>
|
||||||
<p style="font-weight:600">{errMsg}</p>
|
<p style="font-weight:600">{errMsg}</p>
|
||||||
</Island>
|
</Island>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if isLoading}
|
||||||
|
<Loading />
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#each data as service}
|
{#each data as service}
|
||||||
{#if service}
|
{#if service}
|
||||||
<TrainDetail {service} date={new Date(formattedDate)} />
|
<TrainDetail {service} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
@ -149,15 +106,4 @@
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
#dateInput {
|
|
||||||
height: 25px;
|
|
||||||
transform: translateY(-8px) translateX(20px);
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
transform: translateX(20px);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -7,10 +7,8 @@ const cacheName = `ob-${version}`;
|
|||||||
const assets = [...build, ...files, "/service-worker.js"];
|
const assets = [...build, ...files, "/service-worker.js"];
|
||||||
|
|
||||||
const excludePatterns = [
|
const excludePatterns = [
|
||||||
"/static/images/screnshots",
|
"/static/images/screnshots/",
|
||||||
"/images/screenshots",
|
"/images/screenshots",
|
||||||
"/static/images/shortcuts",
|
|
||||||
"/images/shortcuts",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
self.addEventListener("install", (event) => {
|
self.addEventListener("install", (event) => {
|
||||||
|
BIN
static/images/logo/mono-logo-33.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
static/images/logo/square-logo-100.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
static/images/logo/wide_logo_200.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
static/images/logo/wide_logo_250.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
static/images/nre/nre-powered.xcf
Normal file
BIN
static/images/nre/nre-powered_400w.jxl
Normal file
BIN
static/images/nre/nre-powered_400w.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
static/images/nre/nre-powered_400w.webp
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
static/images/nre/nre-powered_800w.jxl
Normal file
BIN
static/images/nre/nre-powered_800w.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
static/images/nre/nre-powered_800w.webp
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
static/images/screenshots/mobile/cis-alert.png
Normal file
After Width: | Height: | Size: 126 KiB |
BIN
static/images/screenshots/mobile/cis-train-detail.png
Normal file
After Width: | Height: | Size: 130 KiB |
BIN
static/images/screenshots/mobile/cis.png
Normal file
After Width: | Height: | Size: 125 KiB |
BIN
static/images/screenshots/mobile/home.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
static/images/screenshots/mobile/location-code.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
static/images/screenshots/mobile/pis-search.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
static/images/screenshots/mobile/reason-code.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
static/images/screenshots/mobile/settings.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
static/images/screenshots/mobile/timetable-detail.png
Normal file
After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 111 KiB |
@ -32,88 +32,130 @@
|
|||||||
],
|
],
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/new_mobile/home.png",
|
"src": "/images/screenshots/mobile/home.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "400x760",
|
"sizes": "270x600",
|
||||||
"form_factor": "narrow",
|
"form_factor": "narrow",
|
||||||
"label": "Home"
|
"label": "Home"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/new_mobile/cis.png",
|
"src": "/images/screenshots/mobile/cis.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "400x760",
|
"sizes": "270x800",
|
||||||
"form_factor": "narrow",
|
"form_factor": "narrow",
|
||||||
"label": "Live departure boards"
|
"label": "Live departure boards"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/new_mobile/timetable-detail.png",
|
"src": "/images/screenshots/mobile/timetable-detail.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "400x760",
|
"sizes": "270x800",
|
||||||
"form_factor": "narrow",
|
"form_factor": "narrow",
|
||||||
"label": "Train details and PIS Codes"
|
"label": "Train details and PIS Codes"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/new_mobile/pis-search.png",
|
"src": "/images/screenshots/mobile/pis-search.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "400x760",
|
"sizes": "270x800",
|
||||||
"form_factor": "narrow",
|
"form_factor": "narrow",
|
||||||
"label": "PIS Codes"
|
"label": "PIS Codes"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"src": "/images/screenshots/mobile/cis-train-detail.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "270x800",
|
||||||
|
"form_factor": "narrow",
|
||||||
|
"label": "Departure board detail"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/images/screenshots/mobile/cis-alert.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "270x800",
|
||||||
|
"form_factor": "narrow",
|
||||||
|
"label": "Service disruption messages"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/images/screenshots/mobile/settings.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "270x800",
|
||||||
|
"form_factor": "narrow",
|
||||||
|
"label": "Customise your settings"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/images/screenshots/mobile/location-code.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "270x800",
|
||||||
|
"form_factor": "narrow",
|
||||||
|
"label": "CRS, TIPLOC, STANOX & NLC Lookup"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/images/screenshots/mobile/reason-code.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "270x800",
|
||||||
|
"form_factor": "narrow",
|
||||||
|
"label": "Delay/Cancellation reason code lookup"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/wide/home.png",
|
"src": "/images/screenshots/wide/home.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "1639x1080",
|
"sizes": "1080x1369",
|
||||||
"form_factor": "wide",
|
"form_factor": "wide",
|
||||||
"label": "Home"
|
"label": "Home"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/wide/cis.png",
|
"src": "/images/screenshots/wide/cis.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "1639x1080",
|
"sizes": "1080x1369",
|
||||||
"form_factor": "wide",
|
"form_factor": "wide",
|
||||||
"label": "Live departure boards"
|
"label": "Live departure boards"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/wide/timetable-detail.png",
|
"src": "/images/screenshots/wide/timetable-detail.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "1639x1080",
|
"sizes": "1080x1369",
|
||||||
"form_factor": "wide",
|
"form_factor": "wide",
|
||||||
"label": "Timetable & PIS"
|
"label": "Timetable & PIS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/wide/pis.png",
|
"src": "/images/screenshots/wide/pis.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "1639x1080",
|
"sizes": "1080x1369",
|
||||||
"form_factor": "wide",
|
"form_factor": "wide",
|
||||||
"label": "PIS Lookup"
|
"label": "PIS Lookup"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/wide/cis-train-detail.png",
|
"src": "/images/screenshots/wide/cis-train-detail.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "1639x1080",
|
"sizes": "1080x1369",
|
||||||
"form_factor": "wide",
|
"form_factor": "wide",
|
||||||
"label": "Live train details"
|
"label": "Live train details"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/wide/cis-alerts.png",
|
"src": "/images/screenshots/wide/cis-alerts.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "1639x1080",
|
"sizes": "1080x1369",
|
||||||
"form_factor": "wide",
|
"form_factor": "wide",
|
||||||
"label": "Live service updates"
|
"label": "Live service updates"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/wide/settings.png",
|
"src": "/images/screenshots/wide/settings.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "1639x1080",
|
"sizes": "1080x1369",
|
||||||
"form_factor": "wide",
|
"form_factor": "wide",
|
||||||
"label": "Customise your settings"
|
"label": "Customise your settings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/screenshots/wide/location-code.png",
|
"src": "/images/screenshots/wide/location-code.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "1639x1080",
|
"sizes": "1080x1369",
|
||||||
"form_factor": "wide",
|
"form_factor": "wide",
|
||||||
"label": "CRS, TIPLOC, STANOX & NLC Lookup"
|
"label": "CRS, TIPLOC, STANOX & NLC Lookup"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/images/screenshots/wide/reason-code.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "1080x1369",
|
||||||
|
"form_factor": "wide",
|
||||||
|
"label": "Delay/Cancellation reason code lookup"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"shortcuts": [
|
"shortcuts": [
|
||||||
|