mirror of
https://github.com/Spythere/srjp-td2.git
synced 2026-05-02 21:18:12 +00:00
feat: journal timetable view mode
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Logger Function
|
||||
log() {
|
||||
local message="$1"
|
||||
local type="$2"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
local color
|
||||
local endcolor="\033[0m"
|
||||
|
||||
case "$type" in
|
||||
"info") color="\033[38;5;79m" ;;
|
||||
"success") color="\033[1;32m" ;;
|
||||
"error") color="\033[1;31m" ;;
|
||||
*) color="\033[1;34m" ;;
|
||||
esac
|
||||
|
||||
echo -e "${color}${timestamp} - ${message}${endcolor}"
|
||||
}
|
||||
|
||||
# Error handler function
|
||||
handle_error() {
|
||||
local exit_code=$1
|
||||
local error_message="$2"
|
||||
log "Error: $error_message (Exit Code: $exit_code)" "error"
|
||||
exit $exit_code
|
||||
}
|
||||
|
||||
# Function to check for command availability
|
||||
command_exists() {
|
||||
command -v "$1" &> /dev/null
|
||||
}
|
||||
|
||||
check_os() {
|
||||
if ! [ -f "/etc/debian_version" ]; then
|
||||
echo "Error: This script is only supported on Debian-based systems."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to Install the script pre-requisites
|
||||
install_pre_reqs() {
|
||||
log "Installing pre-requisites" "info"
|
||||
|
||||
# Run 'apt-get update'
|
||||
if ! apt-get update -y; then
|
||||
handle_error "$?" "Failed to run 'apt-get update'"
|
||||
fi
|
||||
|
||||
# Run 'apt-get install'
|
||||
if ! apt-get install -y apt-transport-https ca-certificates curl gnupg; then
|
||||
handle_error "$?" "Failed to install packages"
|
||||
fi
|
||||
|
||||
if ! mkdir -p /usr/share/keyrings; then
|
||||
handle_error "$?" "Makes sure the path /usr/share/keyrings exist or run ' mkdir -p /usr/share/keyrings' with sudo"
|
||||
fi
|
||||
|
||||
rm -f /usr/share/keyrings/nodesource.gpg || true
|
||||
rm -f /etc/apt/sources.list.d/nodesource.list || true
|
||||
|
||||
# Run 'curl' and 'gpg' to download and import the NodeSource signing key
|
||||
if ! curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /usr/share/keyrings/nodesource.gpg; then
|
||||
handle_error "$?" "Failed to download and import the NodeSource signing key"
|
||||
fi
|
||||
|
||||
# Explicitly set the permissions to ensure the file is readable by all
|
||||
if ! chmod 644 /usr/share/keyrings/nodesource.gpg; then
|
||||
handle_error "$?" "Failed to set correct permissions on /usr/share/keyrings/nodesource.gpg"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to configure the Repo
|
||||
configure_repo() {
|
||||
local node_version=$1
|
||||
|
||||
arch=$(dpkg --print-architecture)
|
||||
if [ "$arch" != "amd64" ] && [ "$arch" != "arm64" ] && [ "$arch" != "armhf" ]; then
|
||||
handle_error "1" "Unsupported architecture: $arch. Only amd64, arm64, and armhf are supported."
|
||||
fi
|
||||
|
||||
echo "deb [arch=$arch signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$node_version nodistro main" | tee /etc/apt/sources.list.d/nodesource.list > /dev/null
|
||||
|
||||
# N|solid Config
|
||||
echo "Package: nsolid" | tee /etc/apt/preferences.d/nsolid > /dev/null
|
||||
echo "Pin: origin deb.nodesource.com" | tee -a /etc/apt/preferences.d/nsolid > /dev/null
|
||||
echo "Pin-Priority: 600" | tee -a /etc/apt/preferences.d/nsolid > /dev/null
|
||||
|
||||
# Nodejs Config
|
||||
echo "Package: nodejs" | tee /etc/apt/preferences.d/nodejs > /dev/null
|
||||
echo "Pin: origin deb.nodesource.com" | tee -a /etc/apt/preferences.d/nodejs > /dev/null
|
||||
echo "Pin-Priority: 600" | tee -a /etc/apt/preferences.d/nodejs > /dev/null
|
||||
|
||||
# Run 'apt-get update'
|
||||
if ! apt-get update -y; then
|
||||
handle_error "$?" "Failed to run 'apt-get update'"
|
||||
else
|
||||
log "Repository configured successfully."
|
||||
log "To install Node.js, run: apt-get install nodejs -y" "info"
|
||||
log "You can use N|solid Runtime as a node.js alternative" "info"
|
||||
log "To install N|solid Runtime, run: apt-get install nsolid -y \n" "success"
|
||||
fi
|
||||
}
|
||||
|
||||
# Define Node.js version
|
||||
NODE_VERSION="23.x"
|
||||
|
||||
# Check OS
|
||||
check_os
|
||||
|
||||
# Main execution
|
||||
install_pre_reqs || handle_error $? "Failed installing pre-requisites"
|
||||
configure_repo "$NODE_VERSION" || handle_error $? "Failed configuring repository"
|
||||
@@ -1,16 +1,68 @@
|
||||
<template>
|
||||
<div class="flex gap-2 mb-2 print:hidden">
|
||||
<button
|
||||
class="p-1 rounded-md"
|
||||
:class="{
|
||||
'bg-zinc-800 hover:bg-zinc-700': globalStore.viewMode == 'active',
|
||||
'bg-green-600 hover:bg-green-500': globalStore.viewMode == 'storage',
|
||||
}"
|
||||
@click="toggleViewMode"
|
||||
>
|
||||
<ArchiveBoxArrowDownIcon class="size-6" />
|
||||
</button>
|
||||
<div class="flex gap-2 justify-between flex-wrap mb-2 print:hidden">
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
:class="`p-1 rounded-md ${
|
||||
globalStore.viewMode == 'active'
|
||||
? 'bg-green-600 hover:bg-green-500'
|
||||
: 'bg-zinc-800 hover:bg-zinc-700'
|
||||
}`"
|
||||
@click="toggleViewMode('active')"
|
||||
>
|
||||
<WifiIcon class="size-6" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
:class="`p-1 rounded-md ${
|
||||
globalStore.viewMode == 'storage'
|
||||
? 'bg-green-600 hover:bg-green-500'
|
||||
: 'bg-zinc-800 hover:bg-zinc-700'
|
||||
}`"
|
||||
@click="toggleViewMode('storage')"
|
||||
>
|
||||
<ArchiveBoxArrowDownIcon class="size-6" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
:class="`p-1 rounded-md ${
|
||||
globalStore.viewMode == 'journal'
|
||||
? 'bg-green-600 hover:bg-green-500'
|
||||
: 'bg-zinc-800 hover:bg-zinc-700'
|
||||
}`"
|
||||
@click="toggleViewMode('journal')"
|
||||
>
|
||||
<CloudIcon class="size-6" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button class="bg-zinc-800 p-1 rounded-md hover:bg-zinc-700 self-end" @click="toggleDarkMode">
|
||||
<MoonIcon v-if="globalStore.darkMode" class="text-white size-6" />
|
||||
<SunIcon v-else class="text-white size-6" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="bg-zinc-800 p-1 rounded-md hover:bg-zinc-700 disabled:opacity-60 disabled:hover:bg-zinc-800"
|
||||
:disabled="globalStore.currentTimetableData == null"
|
||||
@click="openPrintingWindow"
|
||||
>
|
||||
<PrinterIcon class="text-white size-6" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="p-1 rounded-md disabled:opacity-60 disabled:hover:bg-zinc-800"
|
||||
:disabled="globalStore.currentTimetableData == null"
|
||||
:class="{
|
||||
'bg-green-600 hover:bg-green-700': isTimetableSaved,
|
||||
'bg-zinc-800 hover:bg-zinc-700': !isTimetableSaved
|
||||
}"
|
||||
@click="saveToStorage"
|
||||
>
|
||||
<ArrowDownTrayIcon class="text-white size-6" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Active Data Search -->
|
||||
<select
|
||||
name="trains"
|
||||
id="trains-select"
|
||||
@@ -21,45 +73,37 @@
|
||||
@change="selectTrain"
|
||||
>
|
||||
<option :value="null" disabled>
|
||||
{{ apiStore.activeDataStatus == DataStatus.LOADING ? $t('data-loading-text') : $t('train-select-placeholder') }}
|
||||
{{
|
||||
apiStore.activeDataStatus == DataStatus.LOADING
|
||||
? $t('data-loading-text')
|
||||
: $t('train-select-placeholder')
|
||||
}}
|
||||
</option>
|
||||
<option :value="train.id" v-for="train in globalStore.activeTimetableTrains">
|
||||
{{ train.driverName }} | {{ train.timetable?.category }} {{ train.trainNo }} [{{ getRegionNameById(train.region) }}]
|
||||
{{ train.driverName }} | {{ train.timetable?.category }} {{ train.trainNo }} [{{
|
||||
getRegionNameById(train.region)
|
||||
}}]
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<!-- Local Storage Search -->
|
||||
<input
|
||||
type="text"
|
||||
v-if="globalStore.viewMode == 'storage'"
|
||||
v-model="globalStore.timetableSearch"
|
||||
v-model="globalStore.localTimetableSearch"
|
||||
class="bg-zinc-800 p-1 rounded-md print:hidden w-full"
|
||||
:placeholder="$t('train-search-placeholder')"
|
||||
/>
|
||||
|
||||
<button class="bg-zinc-800 p-1 rounded-md hover:bg-zinc-700" @click="toggleDarkMode">
|
||||
<MoonIcon v-if="globalStore.darkMode" class="text-white size-6" />
|
||||
<SunIcon v-else class="text-white size-6" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="bg-zinc-800 p-1 rounded-md hover:bg-zinc-700 disabled:opacity-60 disabled:hover:bg-zinc-800"
|
||||
:disabled="globalStore.currentTimetableData == null"
|
||||
@click="openPrintingWindow"
|
||||
>
|
||||
<PrinterIcon class="text-white size-6" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="p-1 rounded-md disabled:opacity-60 disabled:hover:bg-zinc-800"
|
||||
:disabled="globalStore.currentTimetableData == null"
|
||||
:class="{
|
||||
'bg-green-600 hover:bg-green-700': isTimetableSaved,
|
||||
'bg-zinc-800 hover:bg-zinc-700': !isTimetableSaved,
|
||||
}"
|
||||
@click="saveToStorage"
|
||||
>
|
||||
<ArrowDownTrayIcon class="text-white size-6" />
|
||||
</button>
|
||||
<!-- Journal Serach -->
|
||||
<input
|
||||
type="text"
|
||||
v-else-if="globalStore.viewMode == 'journal'"
|
||||
v-model="globalStore.journalTimetableSearch"
|
||||
@change="fetchJournalTimetables"
|
||||
class="bg-zinc-800 p-1 rounded-md print:hidden w-full"
|
||||
:placeholder="$t('journal-search-placeholder')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -68,9 +112,17 @@ import { computed } from 'vue';
|
||||
import { useApiStore } from '../../stores/api.store';
|
||||
import { DataStatus } from '../../types/api.types';
|
||||
import { useGlobalStore } from '../../stores/global.store';
|
||||
import { PrinterIcon, MoonIcon, SunIcon, ArchiveBoxArrowDownIcon, ArrowDownTrayIcon } from '@heroicons/vue/16/solid';
|
||||
import {
|
||||
PrinterIcon,
|
||||
MoonIcon,
|
||||
SunIcon,
|
||||
ArchiveBoxArrowDownIcon,
|
||||
ArrowDownTrayIcon,
|
||||
CloudIcon,
|
||||
WifiIcon
|
||||
} from '@heroicons/vue/16/solid';
|
||||
import { getRegionNameById } from '../../utils/trainUtils';
|
||||
import type { TimetableData } from '../../types/common.types';
|
||||
import type { TimetableData, ViewMode } from '../../types/common.types';
|
||||
|
||||
// Stores
|
||||
const apiStore = useApiStore();
|
||||
@@ -80,22 +132,26 @@ const globalStore = useGlobalStore();
|
||||
const isTimetableSaved = computed(() => {
|
||||
if (!globalStore.currentTimetableData) return false;
|
||||
|
||||
return Object.keys(globalStore.storageTimetables).includes(`${globalStore.currentTimetableData.timetableId}`);
|
||||
return Object.keys(globalStore.storageTimetables).includes(
|
||||
`${globalStore.currentTimetableData.timetableId}`
|
||||
);
|
||||
});
|
||||
|
||||
// Methods
|
||||
function selectTrain() {
|
||||
if (!apiStore.activeData) return;
|
||||
|
||||
globalStore.selectedActiveTrain = globalStore.activeTimetableTrains.find((train) => train.id == globalStore.selectedTrainId) ?? null;
|
||||
globalStore.selectedActiveTrain =
|
||||
globalStore.activeTimetableTrains.find((train) => train.id == globalStore.selectedTrainId) ??
|
||||
null;
|
||||
|
||||
if (globalStore.selectedActiveTrain != null) {
|
||||
globalStore.generatedDate = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleViewMode() {
|
||||
globalStore.viewMode = globalStore.viewMode == 'active' ? 'storage' : 'active';
|
||||
function toggleViewMode(viewMode: ViewMode) {
|
||||
globalStore.viewMode = viewMode;
|
||||
}
|
||||
|
||||
function toggleDarkMode() {
|
||||
@@ -109,16 +165,22 @@ function saveToStorage() {
|
||||
|
||||
try {
|
||||
const savedTimetablesStorage = localStorage.getItem('savedTimetables');
|
||||
let savedTimetablesJSON: Record<number, TimetableData> = savedTimetablesStorage ? JSON.parse(savedTimetablesStorage) : {};
|
||||
let savedTimetablesJSON: Record<number, TimetableData> = savedTimetablesStorage
|
||||
? JSON.parse(savedTimetablesStorage)
|
||||
: {};
|
||||
|
||||
if (savedTimetablesJSON[globalStore.currentTimetableData.timetableId] !== undefined) {
|
||||
globalStore.selectedStorageTimetable = savedTimetablesJSON[globalStore.currentTimetableData.timetableId];
|
||||
globalStore.selectedStorageTimetable =
|
||||
savedTimetablesJSON[globalStore.currentTimetableData.timetableId];
|
||||
globalStore.viewMode = 'storage';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
savedTimetablesJSON[globalStore.currentTimetableData.timetableId] = { ...globalStore.currentTimetableData, savedTimestamp: Date.now() };
|
||||
savedTimetablesJSON[globalStore.currentTimetableData.timetableId] = {
|
||||
...globalStore.currentTimetableData,
|
||||
savedTimestamp: Date.now()
|
||||
};
|
||||
|
||||
localStorage.setItem('savedTimetables', JSON.stringify(savedTimetablesJSON));
|
||||
globalStore.storageTimetables = savedTimetablesJSON;
|
||||
@@ -127,21 +189,22 @@ function saveToStorage() {
|
||||
|
||||
function openPrintingWindow() {
|
||||
if (globalStore.selectedActiveTrain != null) {
|
||||
const date = `${globalStore.generatedDate!.toLocaleDateString('pl-PL').replace(/\./g, '-')}--${globalStore
|
||||
const date = `${globalStore
|
||||
.generatedDate!.toLocaleDateString('pl-PL')
|
||||
.replace(/\./g, '-')}--${globalStore
|
||||
.generatedDate!.toLocaleTimeString('pl-PL')
|
||||
.replace(/:/g, '-')}`;
|
||||
|
||||
document.title = `${globalStore.selectedActiveTrain.driverName} ; ${globalStore.selectedActiveTrain.timetable!.category} ${
|
||||
globalStore.selectedActiveTrain.trainNo
|
||||
}
|
||||
document.title = `${globalStore.selectedActiveTrain.driverName} ; ${
|
||||
globalStore.selectedActiveTrain.timetable!.category
|
||||
} ${globalStore.selectedActiveTrain.trainNo}
|
||||
${globalStore.selectedActiveTrain.timetable?.route.replace('|', ' - ')} ; ${date}`;
|
||||
}
|
||||
|
||||
window.print();
|
||||
}
|
||||
|
||||
// function refreshData() {
|
||||
// apiStore.fetchActiveData();
|
||||
// selectTrain();
|
||||
// }
|
||||
async function fetchJournalTimetables() {
|
||||
apiStore.fetchJournalTimetables(globalStore.journalTimetableSearch);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<template>
|
||||
<!-- Local -->
|
||||
<div class="my-2 print:hidden" v-if="globalStore.currentTimetableData?.savedTimestamp">
|
||||
<div class="flex gap-2">
|
||||
<div class="flex items-center gap-2 bg-zinc-900 p-1 w-full">
|
||||
@@ -18,11 +19,49 @@
|
||||
</i18n-t>
|
||||
</div>
|
||||
|
||||
<button class="font-bold bg-zinc-900 p-1 hover:bg-zinc-800" @click="removeTimetable(globalStore.currentTimetableData.timetableId)">
|
||||
<button
|
||||
class="font-bold bg-zinc-900 p-1 hover:bg-zinc-800"
|
||||
@click="removeTimetable(globalStore.currentTimetableData.timetableId)"
|
||||
>
|
||||
<TrashIcon class="text-white size-6" />
|
||||
</button>
|
||||
|
||||
<button class="font-bold bg-zinc-900 p-1 hover:bg-zinc-800" @click="globalStore.selectedStorageTimetable = null">
|
||||
<button
|
||||
class="font-bold bg-zinc-900 p-1 hover:bg-zinc-800"
|
||||
@click="globalStore.selectedStorageTimetable = null"
|
||||
>
|
||||
<ArrowUturnLeftIcon class="text-white size-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Journal -->
|
||||
<div class="my-2 print:hidden" v-if="globalStore.currentTimetableData?.journalCreatedAt">
|
||||
<div class="flex gap-2">
|
||||
<div class="flex items-center gap-2 bg-zinc-900 p-1 w-full">
|
||||
<div>
|
||||
<InformationCircleIcon class="size-5" />
|
||||
</div>
|
||||
|
||||
<i18n-t keypath="journal-preview-info" tag="span">
|
||||
<template v-slot:id>
|
||||
<b>#{{ globalStore.currentTimetableData.timetableId }}</b>
|
||||
</template>
|
||||
<template v-slot:driverName>
|
||||
<b>{{ globalStore.currentTimetableData.driverName }}</b>
|
||||
</template>
|
||||
<template v-slot:date>
|
||||
<b>{{
|
||||
new Date(globalStore.currentTimetableData.journalCreatedAt).toLocaleString()
|
||||
}}</b>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="font-bold bg-zinc-900 p-1 hover:bg-zinc-800"
|
||||
@click="globalStore.selectedJournalTimetable = null"
|
||||
>
|
||||
<ArrowUturnLeftIcon class="text-white size-6" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -45,12 +84,14 @@ function removeTimetable(timetableId: number) {
|
||||
|
||||
try {
|
||||
const savedTimetablesStorage = localStorage.getItem('savedTimetables');
|
||||
let savedTimetablesJSON: Record<number, TimetableData> = savedTimetablesStorage ? JSON.parse(savedTimetablesStorage) : {};
|
||||
let savedTimetablesJSON: Record<number, TimetableData> = savedTimetablesStorage
|
||||
? JSON.parse(savedTimetablesStorage)
|
||||
: {};
|
||||
delete savedTimetablesJSON[timetableId];
|
||||
|
||||
localStorage.setItem('savedTimetables', JSON.stringify(savedTimetablesJSON));
|
||||
globalStore.storageTimetables = savedTimetablesJSON;
|
||||
|
||||
|
||||
globalStore.selectedStorageTimetable = null;
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<template>
|
||||
<div
|
||||
:class="{ dark: globalStore.darkMode }"
|
||||
v-if="globalStore.currentTimetableData != null"
|
||||
class="overflow-auto p-1 bg-white print:bg-white dark:bg-zinc-950 print:text-black text-black dark:text-white min-h-full"
|
||||
>
|
||||
<!-- Timetable render based on current view mode -->
|
||||
<div :class="{ dark: globalStore.darkMode }" v-if="globalStore.currentTimetableData != null"
|
||||
class="overflow-auto p-1 bg-white print:bg-white dark:bg-zinc-950 print:text-black text-black dark:text-white min-h-full">
|
||||
<div>
|
||||
<div class="p-1 font-bold w-max">
|
||||
{{ globalStore.currentTimetableData.category }} {{ globalStore.currentTimetableData.trainNo }} {{ $t('headers.relation') }}
|
||||
{{ globalStore.currentTimetableData.category }} {{ globalStore.currentTimetableData.trainNo }} {{
|
||||
$t('headers.relation') }}
|
||||
{{ globalStore.currentTimetableData.route.replace('|', ' - ') }}
|
||||
</div>
|
||||
|
||||
@@ -22,9 +21,8 @@
|
||||
<div>{{ $t('train-select-info') }}</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<StorageView />
|
||||
</div>
|
||||
<LocalStorageView v-else-if="globalStore.viewMode == 'storage'" />
|
||||
<JournalStorageView v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -35,8 +33,9 @@ import { useApiStore } from '../../stores/api.store';
|
||||
import { useGlobalStore } from '../../stores/global.store';
|
||||
import TimetableBody from './TimetableBody.vue';
|
||||
import TimetableHeader from './TimetableHeader.vue';
|
||||
import type { SceneryRoute, StopRow, TimetablePathData } from '../../types/common.types';
|
||||
import StorageView from '../TimetableStorage/StorageView.vue';
|
||||
import { type SceneryRoute, type StopRow, type TimetablePathData } from '../../types/common.types';
|
||||
import LocalStorageView from '../TimetableViews/LocalStorageView.vue';
|
||||
import JournalStorageView from '../TimetableViews/JournalStorageView.vue';
|
||||
|
||||
const globalStore = useGlobalStore();
|
||||
const apiStore = useApiStore();
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div>API STORAGE</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
@@ -1,56 +0,0 @@
|
||||
<template>
|
||||
<div class="text-white">
|
||||
<div v-if="globalStore.selectedStorageTimetable == null && Object.keys(globalStore.storageTimetables).length == 0">
|
||||
<div class="font-bold text-xl">{{ $t('storage-empty-header') }}</div>
|
||||
<div>{{ $t('storage-empty-info') }}</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div class="font-bold p-2 bg-zinc-700 mb-3">
|
||||
<div class="text-2xl">{{ $t('storage-preview-title') }}</div>
|
||||
<div class="flex gap-2 justify-center">
|
||||
<template v-for="(mode, i) in storageModeList">
|
||||
<span v-if="i != 0">•</span>
|
||||
<button class="hover:text-green-300" :class="{ 'underline text-green-300': currentStorageModeIndex == i }" @click="selectStorageMode(i)">
|
||||
{{ $t(`storage-mode.${mode.key}`) }}
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current storage mode component -->
|
||||
<component :is="storageModeList[currentStorageModeIndex].component" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useGlobalStore } from '../../stores/global.store';
|
||||
import { StorageMode } from '../../types/common.types';
|
||||
import { ref, type Component, type Ref } from 'vue';
|
||||
import LocalStorage from '../TimetableStorage/LocalStorage.vue';
|
||||
import ApiStorage from '../TimetableStorage/ApiStorage.vue';
|
||||
|
||||
interface CurrentStorageMode {
|
||||
key: StorageMode;
|
||||
component: Component;
|
||||
}
|
||||
|
||||
const globalStore = useGlobalStore();
|
||||
const currentStorageModeIndex: Ref<number> = ref(0);
|
||||
|
||||
const storageModeList: CurrentStorageMode[] = [
|
||||
{
|
||||
key: StorageMode.LOCAL,
|
||||
component: LocalStorage,
|
||||
},
|
||||
{
|
||||
key: StorageMode.API,
|
||||
component: ApiStorage,
|
||||
},
|
||||
];
|
||||
|
||||
function selectStorageMode(index: number) {
|
||||
currentStorageModeIndex.value = index;
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="text-white">
|
||||
<h2 class="font-bold p-2 bg-zinc-700 mb-3 text-2xl">
|
||||
{{ $t('journal-preview-title') }}
|
||||
</h2>
|
||||
|
||||
<div v-if="apiStore.journalDataStatus == DataStatus.LOADING" class="bg-zinc-900 p-2">
|
||||
{{ $t('data-loading-text') }}
|
||||
</div>
|
||||
|
||||
<div v-else-if="apiStore.journalDataStatus == DataStatus.ERROR" class="bg-red-500 p-2">
|
||||
{{ $t('data-loading-error-text') }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="!apiStore.journalTimetablesData"
|
||||
class="text-zinc-400 mt-2"
|
||||
v-html="$t('journal-empty-info')"
|
||||
></div>
|
||||
|
||||
<div v-else-if="apiStore.journalTimetablesData.length == 0">
|
||||
<p class="text-zinc-300 mb-2">
|
||||
{{ $t('journal-no-data') }}
|
||||
</p>
|
||||
|
||||
<b class="text-red-300">
|
||||
{{ $t('journal-reminder-text') }}
|
||||
</b>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<ul>
|
||||
<li
|
||||
v-for="timetable in apiStore.journalTimetablesData"
|
||||
class="flex gap-1 w-full my-2"
|
||||
@click="fetchTimetableDetails(timetable.id)"
|
||||
>
|
||||
<button class="bg-zinc-900 p-2 w-full cursor-pointer hover:bg-zinc-800 text-left">
|
||||
<div class="text-zinc-300">
|
||||
#{{ timetable.id }} •
|
||||
{{ new Date(timetable.createdAt!).toLocaleString() }}
|
||||
</div>
|
||||
<b>
|
||||
{{ timetable.driverName }} | {{ timetable.trainCategoryCode }}
|
||||
{{ timetable.trainNo }}
|
||||
</b>
|
||||
{{ timetable.route.replace('|', ' > ') }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div v-if="apiStore.journalTimetablesData.length > 0">
|
||||
<hr class="border-t-2 border-t-gray-500" />
|
||||
|
||||
<p class="text-zinc-400 text-sm">
|
||||
{{ $t('journal-footer-text') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useApiStore } from '../../stores/api.store';
|
||||
import { DataStatus } from '../../types/api.types';
|
||||
|
||||
const apiStore = useApiStore();
|
||||
|
||||
function fetchTimetableDetails(id: number) {
|
||||
apiStore.fetchJournalTimetableDetails(id);
|
||||
}
|
||||
</script>
|
||||
+45
-14
@@ -1,16 +1,42 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="font-bold p-2 bg-zinc-800 mb-3" v-if="filteredTimetables.length == 0">{{ $t('storage-preview-empty') }}</div>
|
||||
<div class="text-white">
|
||||
<div class="font-bold p-2 bg-zinc-700 mb-3">
|
||||
<div class="text-2xl">{{ $t('storage-preview-title') }}</div>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<div
|
||||
v-if="
|
||||
globalStore.selectedStorageTimetable == null &&
|
||||
Object.keys(globalStore.storageTimetables).length == 0
|
||||
"
|
||||
class="text-zinc-400"
|
||||
>
|
||||
{{ $t('storage-empty-info') }}
|
||||
</div>
|
||||
|
||||
<div class="font-bold p-2 bg-zinc-800 mb-3" v-else-if="filteredTimetables.length == 0">
|
||||
{{ $t('storage-preview-empty') }}
|
||||
</div>
|
||||
|
||||
<ul v-else>
|
||||
<li v-for="timetable in filteredTimetables" class="flex gap-1 w-full my-2">
|
||||
<button class="bg-zinc-900 p-2 w-full cursor-pointer hover:bg-zinc-800 text-left" @click="selectTimetable(timetable)">
|
||||
<div class="text-zinc-300">#{{ timetable.timetableId }} • {{ new Date(timetable.savedTimestamp!).toLocaleString() }}</div>
|
||||
<b>{{ timetable.driverName }} | {{ timetable.category }} {{ timetable.trainNo }}</b> {{ timetable.route.replace('|', ' > ') }}
|
||||
<button
|
||||
class="bg-zinc-900 p-2 w-full cursor-pointer hover:bg-zinc-800 text-left"
|
||||
@click="selectTimetable(timetable)"
|
||||
>
|
||||
<div class="text-zinc-300">
|
||||
#{{ timetable.timetableId }} •
|
||||
{{ new Date(timetable.savedTimestamp!).toLocaleString() }}
|
||||
</div>
|
||||
<b>{{ timetable.driverName }} | {{ timetable.category }} {{ timetable.trainNo }}</b>
|
||||
{{ timetable.route.replace('|', ' > ') }}
|
||||
</button>
|
||||
|
||||
<button class="bg-zinc-900 p-2 hover:bg-zinc-800" @click="removeTimetable(timetable.timetableId)">
|
||||
<TrashIcon class="size-5 text-white" />
|
||||
<button
|
||||
class="bg-zinc-900 p-2 hover:bg-zinc-800"
|
||||
@click="removeTimetable(timetable.timetableId)"
|
||||
>
|
||||
<TrashIcon class="size-6 text-white" />
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -18,11 +44,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useGlobalStore } from '../../stores/global.store';
|
||||
import { computed } from 'vue';
|
||||
import { TrashIcon } from '@heroicons/vue/16/solid';
|
||||
import type { TimetableData } from '../../types/common.types';
|
||||
import { TrashIcon } from '@heroicons/vue/16/solid';
|
||||
|
||||
const globalStore = useGlobalStore();
|
||||
const i18n = useI18n();
|
||||
@@ -30,11 +56,11 @@ const i18n = useI18n();
|
||||
const filteredTimetables = computed(() => {
|
||||
let timetables = Object.values(globalStore.storageTimetables);
|
||||
|
||||
if (globalStore.timetableSearch.length != 0)
|
||||
if (globalStore.localTimetableSearch.length != 0)
|
||||
timetables = timetables.filter((st) =>
|
||||
`${st.timetableId} ${st.driverName} ${st.route} ${st.category} ${st.trainNo}`
|
||||
.toLocaleLowerCase()
|
||||
.includes(globalStore.timetableSearch.toLocaleLowerCase())
|
||||
.includes(globalStore.localTimetableSearch.toLocaleLowerCase())
|
||||
);
|
||||
|
||||
timetables.sort((a, b) => {
|
||||
@@ -55,11 +81,16 @@ function removeTimetable(timetableId: number) {
|
||||
|
||||
try {
|
||||
const savedTimetablesStorage = localStorage.getItem('savedTimetables');
|
||||
let savedTimetablesJSON: Record<number, TimetableData> = savedTimetablesStorage ? JSON.parse(savedTimetablesStorage) : {};
|
||||
let savedTimetablesJSON: Record<number, TimetableData> = savedTimetablesStorage
|
||||
? JSON.parse(savedTimetablesStorage)
|
||||
: {};
|
||||
delete savedTimetablesJSON[timetableId];
|
||||
|
||||
localStorage.setItem('savedTimetables', JSON.stringify(savedTimetablesJSON));
|
||||
|
||||
globalStore.storageTimetables = savedTimetablesJSON;
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
+35
-29
@@ -1,30 +1,36 @@
|
||||
{
|
||||
"data-loading-text": "Loading data...",
|
||||
"train-select-placeholder": "Choose active train from the list",
|
||||
"train-select-info": "Choose active train to generate SRJP timetable",
|
||||
"train-search-placeholder": "Enter TT details (number, route, user)",
|
||||
"headers": {
|
||||
"line_no": "Line\nno.",
|
||||
"line_km": "Km",
|
||||
"station": "Station",
|
||||
"time": "Time",
|
||||
"loco_1": "Loco I",
|
||||
"loco_2": "Loco II",
|
||||
"loco_3": "Loco III",
|
||||
"mass": "Loco load",
|
||||
"length": "Train len.",
|
||||
"vmax": "Vmax",
|
||||
"relation": "Route"
|
||||
},
|
||||
"storage-empty-header": "ARCHIVED TIMETABLES SEARCH MODE",
|
||||
"storage-empty-info": "Timetables will be shown here after their archiving.",
|
||||
"storage-preview-title": "ARCHIVED TIMETABLES",
|
||||
"storage-preview-empty": "No entries found for given parameters",
|
||||
"storage-preview-info": "Archived timetable {id} for user {driverName} from: {date}",
|
||||
"storage-preview-button-text": "Return",
|
||||
"delete-timetable-confirm": "Are you sure that you want to delete this timetable?",
|
||||
"storage-mode": {
|
||||
"local": "LOCALLY",
|
||||
"api": "STACJOWNIK"
|
||||
}
|
||||
}
|
||||
"data-loading-text": "Loading data...",
|
||||
"data-loading-error-text": "Oops! An error occurent while loading data from the server!",
|
||||
"train-select-placeholder": "Choose active train from the list",
|
||||
"train-select-info": "Choose active train to generate SRJP timetable",
|
||||
"train-search-placeholder": "Enter TT details (number, route, user)",
|
||||
"headers": {
|
||||
"line_no": "Line\nno.",
|
||||
"line_km": "Km",
|
||||
"station": "Station",
|
||||
"time": "Time",
|
||||
"loco_1": "Loco I",
|
||||
"loco_2": "Loco II",
|
||||
"loco_3": "Loco III",
|
||||
"mass": "Loco load",
|
||||
"length": "Train len.",
|
||||
"vmax": "Vmax",
|
||||
"relation": "Route"
|
||||
},
|
||||
"storage-empty-header": "ARCHIVED TIMETABLES SEARCH MODE",
|
||||
"storage-empty-info": "Timetables will be shown here after their archiving.",
|
||||
"storage-preview-title": "ARCHIVED TIMETABLES",
|
||||
"storage-preview-empty": "No entries found for given parameters",
|
||||
"storage-preview-info": "Archived timetable {id} for user {driverName} from: {date}",
|
||||
"storage-preview-button-text": "Return",
|
||||
"delete-timetable-confirm": "Are you sure that you want to delete this timetable?",
|
||||
|
||||
"journal-preview-title": "TIMETABLES JOURNAL",
|
||||
"journal-empty-info": "Enter timetable details in the text field above - it may be: <br> id (#number); nickname (nick:Spythere); date (date:11.01.2025); starting point (from:Krnów)<br>Up to 15 newest timetables will be shown.",
|
||||
"journal-search-placeholder": "nick:Spythere date:02.04.2025 from:Krnów to:Biała_Sudecka",
|
||||
"journal-preview-info": "Historical timetable {id} for user {driverName} from: {date}",
|
||||
|
||||
"journal-no-data": "No data for the current search! Check if the data you entered is correct.",
|
||||
"journal-reminder-text": "Warning: detailed timetables data for SRJP purpose are collected since 1st February 2025 and only for users who support Stacjownik project!",
|
||||
"journal-footer-text": "Detailed timetables data for SRJP purpose are collected since 1st February 2025 and only for users who support Stacjownik project!"
|
||||
}
|
||||
|
||||
+35
-29
@@ -1,30 +1,36 @@
|
||||
{
|
||||
"data-loading-text": "Ładowanie danych...",
|
||||
"train-select-placeholder": "Wybierz pociąg z listy",
|
||||
"train-select-info": "Wybierz aktywny pociąg, aby wygenerować SRJP",
|
||||
"train-search-placeholder": "Wpisz szczegóły RJ (nr, relacja, gracz)",
|
||||
"headers": {
|
||||
"line_no": "Nr\nlinii",
|
||||
"line_km": "Km",
|
||||
"station": "Stacja",
|
||||
"time": "Godzina",
|
||||
"loco_1": "Lok I",
|
||||
"loco_2": "Lok II",
|
||||
"loco_3": "Lok III",
|
||||
"mass": "Obc. lok.",
|
||||
"length": "Dł. poc.",
|
||||
"vmax": "Vmax",
|
||||
"relation": "Relacja"
|
||||
},
|
||||
"storage-empty-header": "TRYB WYSZUKIWANA ZAPISANYCH ROZKŁADÓW JAZDY",
|
||||
"storage-empty-info": "Użyj funkcji zapisu rozkładu jazdy, aby go tutaj wyświetlić.",
|
||||
"storage-preview-title": "ZAPISANE ROZKŁADY JAZDY",
|
||||
"storage-preview-empty": "Nie znaleziono żadnych wpisów dla podanych parametrów",
|
||||
"storage-preview-info": "Rozkład archiwalny {id} maszynisty {driverName} z dnia {date}",
|
||||
"storage-preview-button-text": "Powróć",
|
||||
"delete-timetable-confirm": "Czy na pewno chcesz usunąć ten rozkład jazdy z archiwum?",
|
||||
"storage-mode": {
|
||||
"local": "LOKALNIE",
|
||||
"api": "STACJOWNIK"
|
||||
}
|
||||
}
|
||||
"data-loading-text": "Ładowanie danych...",
|
||||
"data-loading-error-text": "Ups! Wystąpił błąd podczas pobierania danych z serwera!",
|
||||
"train-select-placeholder": "Wybierz pociąg z listy",
|
||||
"train-select-info": "Wybierz aktywny pociąg, aby wygenerować SRJP",
|
||||
"train-search-placeholder": "Wpisz szczegóły RJ (nr, relacja, gracz)",
|
||||
"headers": {
|
||||
"line_no": "Nr\nlinii",
|
||||
"line_km": "Km",
|
||||
"station": "Stacja",
|
||||
"time": "Godzina",
|
||||
"loco_1": "Lok I",
|
||||
"loco_2": "Lok II",
|
||||
"loco_3": "Lok III",
|
||||
"mass": "Obc. lok.",
|
||||
"length": "Dł. poc.",
|
||||
"vmax": "Vmax",
|
||||
"relation": "Relacja"
|
||||
},
|
||||
"storage-empty-header": "TRYB WYSZUKIWANA ZAPISANYCH ROZKŁADÓW JAZDY",
|
||||
"storage-empty-info": "Użyj funkcji zapisu rozkładu jazdy, aby go tutaj wyświetlić.",
|
||||
"storage-preview-title": "ZAPISANE ROZKŁADY JAZDY",
|
||||
"storage-preview-empty": "Nie znaleziono żadnych wpisów dla podanych parametrów",
|
||||
"storage-preview-info": "Rozkład archiwalny {id} maszynisty {driverName} z dnia {date}",
|
||||
"storage-preview-button-text": "Powróć",
|
||||
"delete-timetable-confirm": "Czy na pewno chcesz usunąć ten rozkład jazdy z archiwum?",
|
||||
|
||||
"journal-preview-title": "DZIENNIK ROZKŁADÓW JAZDY",
|
||||
"journal-empty-info": "Wpisz dane rozkładu korzystając z pola tekstowego powyżej - mogą nimi być: <br> id (#numer); nickname (nick:Spythere); data (date:11.01.2025); punkt startowy (from:Krnów)<br>W przypadku wielu rozkładów jazdy wyświetli się maks. 15 najnowszych.",
|
||||
"journal-search-placeholder": "nick:Spythere date:02.04.2025 from:Krnów to:Biała_Sudecka",
|
||||
"journal-preview-info": "Rozkład historyczny {id} maszynisty {driverName} z dnia {date}",
|
||||
|
||||
"journal-no-data": "Brak wyników dla obecnego wyszukiwania! Sprawdź czy wpisałeś poprawnie dane.",
|
||||
"journal-reminder-text": "Uwaga: szczegółowe rozkłady jazdy są zapisywane od 1 lutego 2025r. wyłącznie dla osób wspierających projekt Stacjownika!",
|
||||
"journal-footer-text": "Szczegółowe dane o rozkładach jazdy do wygenerowania SRJP są zbierane od 1 lutego 2025r. wyłącznie dla maszynistów wspierających projekt Stacjownika!"
|
||||
}
|
||||
|
||||
+85
-5
@@ -1,8 +1,19 @@
|
||||
import type { AxiosInstance } from 'axios';
|
||||
import axios from 'axios';
|
||||
import { defineStore } from 'pinia';
|
||||
import { DataStatus, type ActiveDataResponse, type SceneriesDataResponse } from '../types/api.types';
|
||||
import type { ActiveData, SceneryData } from '../types/common.types';
|
||||
import {
|
||||
DataStatus,
|
||||
type ActiveDataResponse,
|
||||
type SceneriesDataResponse,
|
||||
type JournalTimetablesShortResponse
|
||||
} from '../types/api.types';
|
||||
import type {
|
||||
ActiveData,
|
||||
JournalTimetableDetailed,
|
||||
JournalTimetableShort,
|
||||
SceneryData
|
||||
} from '../types/common.types';
|
||||
import { useGlobalStore } from './global.store';
|
||||
|
||||
export const useApiStore = defineStore('api', {
|
||||
state() {
|
||||
@@ -11,11 +22,13 @@ export const useApiStore = defineStore('api', {
|
||||
|
||||
activeData: null as ActiveData | null,
|
||||
sceneryData: null as SceneryData[] | null,
|
||||
journalTimetablesData: null as JournalTimetableShort[] | null,
|
||||
|
||||
outdatedTimerId: -1,
|
||||
isActiveDataOutdated: false,
|
||||
|
||||
activeDataStatus: DataStatus.LOADING,
|
||||
journalDataStatus: DataStatus.SUCCESS
|
||||
};
|
||||
},
|
||||
|
||||
@@ -37,13 +50,12 @@ export const useApiStore = defineStore('api', {
|
||||
}
|
||||
|
||||
this.client = axios.create({
|
||||
baseURL,
|
||||
baseURL
|
||||
});
|
||||
|
||||
this.fetchSceneriesData();
|
||||
await this.fetchActiveData();
|
||||
|
||||
|
||||
setInterval(() => {
|
||||
this.fetchActiveData();
|
||||
}, 25000);
|
||||
@@ -76,5 +88,73 @@ export const useApiStore = defineStore('api', {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
async fetchJournalTimetables(searchValue: string) {
|
||||
// if (searchValue.trim().length == 0) {
|
||||
// this.journalDataStatus = DataStatus.SUCCESS;
|
||||
// this.journalTimetablesData = null;
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
let searchObj: Record<string, any> = {};
|
||||
const searchParams = searchValue.split(' ');
|
||||
|
||||
searchParams.forEach((param) => {
|
||||
const [key, value] = param.split(':');
|
||||
|
||||
if (key == 'nick') searchObj['driverName'] = value;
|
||||
else if (key == 'date') {
|
||||
let dateFromStr = new Date(value).toISOString();
|
||||
|
||||
let dateTo = new Date(dateFromStr);
|
||||
dateTo.setDate(dateTo.getDate() + 1);
|
||||
|
||||
searchObj['dateFrom'] = dateFromStr;
|
||||
searchObj['dateTo'] = dateTo.toISOString();
|
||||
} else if (key == 'from') searchObj['issuedFrom'] = value.replace(/_/g, ' ');
|
||||
else if (key == 'to') searchObj['terminatingAt'] = value.replace(/_/g, ' ');
|
||||
});
|
||||
|
||||
searchObj['hasStopsDetails'] = 1;
|
||||
searchObj['returnType'] = 'short';
|
||||
|
||||
try {
|
||||
this.journalDataStatus = DataStatus.LOADING;
|
||||
|
||||
const response = (
|
||||
await this.client!.get<JournalTimetablesShortResponse>('/api/getTimetables', {
|
||||
params: searchObj
|
||||
})
|
||||
).data;
|
||||
|
||||
this.journalDataStatus = DataStatus.SUCCESS;
|
||||
this.journalTimetablesData = response;
|
||||
} catch (error) {
|
||||
this.journalDataStatus = DataStatus.ERROR;
|
||||
this.journalTimetablesData = null;
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
async fetchJournalTimetableDetails(id: number) {
|
||||
const globalStore = useGlobalStore();
|
||||
|
||||
try {
|
||||
const response = (
|
||||
await this.client!.get<JournalTimetableDetailed[]>('/api/getTimetables', {
|
||||
params: {
|
||||
timetableId: id,
|
||||
hasStopsDetails: 1
|
||||
}
|
||||
})
|
||||
).data;
|
||||
|
||||
if (response.length > 0) globalStore.selectedJournalTimetable = response[0];
|
||||
} catch (error) {
|
||||
globalStore.selectedJournalTimetable = null;
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+56
-13
@@ -1,6 +1,12 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { useApiStore } from './api.store';
|
||||
import type { ActiveTrain, TimetableData, ViewMode } from '../types/common.types';
|
||||
import type {
|
||||
ActiveTrain,
|
||||
JournalTimetableDetailed,
|
||||
JournalTimetableShort,
|
||||
TimetableData,
|
||||
ViewMode
|
||||
} from '../types/common.types';
|
||||
import { unitNameCorrections } from '../utils/trainUtils';
|
||||
|
||||
export const useGlobalStore = defineStore('global', {
|
||||
@@ -11,6 +17,8 @@ export const useGlobalStore = defineStore('global', {
|
||||
selectedTrainId: null as string | null,
|
||||
selectedActiveTrain: null as ActiveTrain | null,
|
||||
selectedStorageTimetable: null as TimetableData | null,
|
||||
selectedJournalTimetable: null as JournalTimetableDetailed | null,
|
||||
|
||||
storageTimetables: {} as Record<number, TimetableData>,
|
||||
|
||||
timetableWarnings: [] as string[],
|
||||
@@ -18,9 +26,10 @@ export const useGlobalStore = defineStore('global', {
|
||||
generatedDate: null as Date | null,
|
||||
generatedMs: 0,
|
||||
|
||||
timetableSearch: '',
|
||||
localTimetableSearch: '',
|
||||
journalTimetableSearch: '',
|
||||
|
||||
showSettings: false,
|
||||
showSettings: false
|
||||
}),
|
||||
getters: {
|
||||
activeTimetableTrains() {
|
||||
@@ -28,7 +37,9 @@ export const useGlobalStore = defineStore('global', {
|
||||
|
||||
if (!apiStore.activeData) return [];
|
||||
|
||||
return apiStore.activeData.trains.filter((train) => train.timetable).sort((t1, t2) => t1.driverName.localeCompare(t2.driverName, 'pl-PL'));
|
||||
return apiStore.activeData.trains
|
||||
.filter((train) => train.timetable)
|
||||
.sort((t1, t2) => t1.driverName.localeCompare(t2.driverName, 'pl-PL'));
|
||||
},
|
||||
|
||||
currentTimetableData(): TimetableData | null {
|
||||
@@ -52,12 +63,14 @@ export const useGlobalStore = defineStore('global', {
|
||||
trainMaxSpeed: selectedTrain.timetable.trainMaxSpeed,
|
||||
timetableId: selectedTrain.timetable.timetableId,
|
||||
stopListString: selectedTrain.timetable.stopList
|
||||
.filter((stop) => stop.mainStop || (/^podg|po|pe$/.test(stop.stopNameRAW)))
|
||||
.filter((stop) => stop.mainStop || /^podg|po|pe$/.test(stop.stopNameRAW))
|
||||
.map(
|
||||
(stop) =>
|
||||
`${stop.arrivalLine ?? ''};${stop.arrivalTimestamp};${stop.stopNameRAW};${stop.stopTime ? stop.stopTime + '_' + stop.stopType : ''};${
|
||||
stop.mainStop
|
||||
};${stop.stopDistance};${stop.departureTimestamp};${stop.departureLine ?? ''}`
|
||||
`${stop.arrivalLine ?? ''};${stop.arrivalTimestamp};${stop.stopNameRAW};${
|
||||
stop.stopTime ? stop.stopTime + '_' + stop.stopType : ''
|
||||
};${stop.mainStop};${stop.stopDistance};${stop.departureTimestamp};${
|
||||
stop.departureLine ?? ''
|
||||
}`
|
||||
)
|
||||
.join('~~'),
|
||||
headUnits: selectedTrain.stockString
|
||||
@@ -68,13 +81,43 @@ export const useGlobalStore = defineStore('global', {
|
||||
const unitName = s.slice(0, s.indexOf('-'));
|
||||
|
||||
return unitNameCorrections[unitName] ?? unitName;
|
||||
}),
|
||||
})
|
||||
};
|
||||
} else if (this.viewMode == 'journal') {
|
||||
const selectedTimetable = this.selectedJournalTimetable;
|
||||
|
||||
if (!selectedTimetable || !selectedTimetable.stopListString) return null;
|
||||
|
||||
return {
|
||||
journalCreatedAt: new Date(selectedTimetable.createdAt).getTime(),
|
||||
trainNo: selectedTimetable.trainNo,
|
||||
mass: selectedTimetable.stockMass,
|
||||
length: selectedTimetable.stockLength,
|
||||
driverId: selectedTimetable.driverId,
|
||||
driverName: selectedTimetable.driverName,
|
||||
category: selectedTimetable.trainCategoryCode,
|
||||
hasDangerousCargo: selectedTimetable.hasDangerousCargo,
|
||||
hasExtraDeliveries: selectedTimetable.hasExtraDeliveries,
|
||||
warningNotes: selectedTimetable.warningNotes,
|
||||
path: selectedTimetable.path,
|
||||
route: selectedTimetable.route,
|
||||
trainMaxSpeed: selectedTimetable.trainMaxSpeed,
|
||||
timetableId: selectedTimetable.id,
|
||||
stopListString: selectedTimetable.stopListString,
|
||||
headUnits: selectedTimetable.stockString
|
||||
.split(';')
|
||||
.slice(0, 3)
|
||||
.filter((s, i) => i == 0 || /-\d+$/.test(s))
|
||||
.map((s) => {
|
||||
const unitName = s.slice(0, s.indexOf('-'));
|
||||
|
||||
return unitNameCorrections[unitName] ?? unitName;
|
||||
})
|
||||
};
|
||||
} else {
|
||||
const selectedStorageTimetable = this.selectedStorageTimetable;
|
||||
return selectedStorageTimetable;
|
||||
return this.selectedStorageTimetable;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
actions: {},
|
||||
actions: {}
|
||||
});
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import type { ActiveData, SceneryData } from './common.types';
|
||||
import type { ActiveData, JournalTimetableShort, SceneryData } from './common.types';
|
||||
|
||||
export type ActiveDataResponse = ActiveData;
|
||||
|
||||
export type SceneriesDataResponse = SceneryData[];
|
||||
|
||||
export type JournalTimetablesShortResponse = JournalTimetableShort[];
|
||||
|
||||
export enum DataStatus {
|
||||
'INIT' = -1,
|
||||
'LOADING' = 0,
|
||||
'SUCCESS' = 1,
|
||||
'ERROR' = 2,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type ViewMode = 'active' | 'storage';
|
||||
export type ViewMode = 'active' | 'storage' | 'journal';
|
||||
|
||||
export enum StorageMode {
|
||||
LOCAL = 'local',
|
||||
@@ -245,7 +245,7 @@ export interface JournalTimetableDetailed extends JournalTimetableShort {
|
||||
warningNotes: string;
|
||||
hasDangerousCargo: boolean;
|
||||
hasExtraDeliveries: boolean;
|
||||
stopListString: any;
|
||||
stopListString?: string;
|
||||
}
|
||||
|
||||
export interface TimetableData {
|
||||
@@ -265,4 +265,5 @@ export interface TimetableData {
|
||||
stopListString: string;
|
||||
headUnits: string[];
|
||||
savedTimestamp?: number;
|
||||
journalCreatedAt?: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user