mirror of
https://github.com/Spythere/srjp-td2.git
synced 2026-05-03 13:38:12 +00:00
Compare commits
1 Commits
v1.1.3
...
feat/journal
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e888544c1 |
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="text-white">
|
||||
<!-- <div v-if="apiStore.journalTimetables == null">
|
||||
<div class="font-bold text-xl">{{ $t('storage-empty-header') }}</div>
|
||||
<div>{{ $t('storage-empty-info') }}</div>
|
||||
</div> -->
|
||||
|
||||
<div class="font-bold text-2xl p-2 bg-zinc-800">DZIENNIK SRJP</div>
|
||||
|
||||
<div v-if="apiStore.journalTimetables == null" class="text-md mt-2 p-2 bg-zinc-900">
|
||||
Wyszukaj gracza w polu powyżej, aby pokazać jego najnowsze SRJP.
|
||||
<!-- <div class="text-sm text-red-400 mt-2 flex flex-wrap md:flex-nowrap justify-center items-center gap-1">
|
||||
<ExclamationCircleIcon class="size-8 min-w-8" />
|
||||
<span>
|
||||
Rozkłady z danymi do wygenerowania SRJP są dostępne tylko dla wspierających twórczość
|
||||
<a class="underline hover:text-red-200 transition-colors" href="https://td2.info.pl/profile/?u=20777" target="_blank">@Spythere</a> dla
|
||||
symulatora TD2!
|
||||
</span>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!-- <div class="font-bold text-xl p-2 bg-zinc-700 mb-3">{{ $t('storage-preview-title') }}</div> -->
|
||||
<!-- <div class="font-bold p-2 bg-zinc-800 mb-3" v-if="filteredTimetables.length == 0">{{ $t('storage-preview-empty') }}</div> -->
|
||||
|
||||
<li v-for="timetable in apiStore.journalTimetables" 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.id)">
|
||||
<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>
|
||||
|
||||
<!-- <button class="bg-zinc-900 p-2 hover:bg-zinc-800" @click="removeTimetable(timetable.timetableId)">
|
||||
<TrashIcon class="size-5 text-white" />
|
||||
</button> -->
|
||||
</li>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useGlobalStore } from '../../stores/global.store';
|
||||
import { useApiStore } from '../../stores/api.store';
|
||||
import { onMounted } from 'vue';
|
||||
import { ExclamationCircleIcon } from '@heroicons/vue/16/solid';
|
||||
|
||||
const globalStore = useGlobalStore();
|
||||
const apiStore = useApiStore();
|
||||
const i18n = useI18n();
|
||||
|
||||
onMounted(() => {
|
||||
// apiStore.fetchTimetableHistoryList();
|
||||
});
|
||||
|
||||
function selectTimetable(timetableId: number) {}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -1,15 +1,56 @@
|
||||
<template>
|
||||
<div class="flex gap-2 mb-2">
|
||||
<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="relative" @focusin="isMenuOpen = true" @keydown.esc="isMenuOpen = false">
|
||||
<button
|
||||
class="p-1 rounded-md flex gap-2"
|
||||
:class="{
|
||||
'bg-zinc-800 hover:bg-zinc-700': isMenuOpen == false,
|
||||
'bg-green-600 hover:bg-green-500': isMenuOpen == true,
|
||||
}"
|
||||
@click="isMenuOpen = !isMenuOpen"
|
||||
>
|
||||
<Bars3Icon class="size-6" />
|
||||
</button>
|
||||
|
||||
<div class="fixed z-20 left-0 top-0 w-screen h-screen" v-if="isMenuOpen" @click="isMenuOpen = false"></div>
|
||||
|
||||
<div class="absolute z-30 top-full left-0 w-36 p-1 mt-2 flex flex-col gap-1 bg-zinc-600 rounded-md" v-if="isMenuOpen">
|
||||
<button
|
||||
class="p-1 rounded-md flex gap-2"
|
||||
:class="{
|
||||
'bg-zinc-950 hover:bg-zinc-800': globalStore.viewMode != 'active',
|
||||
'bg-green-600 hover:bg-green-500': globalStore.viewMode == 'active',
|
||||
}"
|
||||
@click="toggleViewMode('active')"
|
||||
>
|
||||
<WifiIcon class="size-6" /> <span> Aktywne RJ</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="p-1 rounded-md flex gap-2"
|
||||
:class="{
|
||||
'bg-zinc-950 hover:bg-zinc-800': globalStore.viewMode != 'storage',
|
||||
'bg-green-600 hover:bg-green-500': globalStore.viewMode == 'storage',
|
||||
}"
|
||||
@click="toggleViewMode('storage')"
|
||||
>
|
||||
<ArchiveBoxArrowDownIcon class="size-6" /> Zapisane RJ
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="p-1 rounded-md flex gap-2"
|
||||
:class="{
|
||||
'bg-zinc-950 hover:bg-zinc-800': globalStore.viewMode != 'journal',
|
||||
'bg-green-600 hover:bg-green-500': globalStore.viewMode == 'journal',
|
||||
}"
|
||||
@click="toggleViewMode('journal')"
|
||||
>
|
||||
<CloudArrowDownIcon class="size-6" /> Dziennik RJ
|
||||
</button>
|
||||
|
||||
<!-- <button class="m-0 p-0" @focus="isMenuOpen = false"></button> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<select
|
||||
name="trains"
|
||||
@@ -31,11 +72,31 @@
|
||||
<input
|
||||
type="text"
|
||||
v-if="globalStore.viewMode == 'storage'"
|
||||
v-model="globalStore.timetableSearch"
|
||||
v-model="globalStore.storageTimetableSearch"
|
||||
class="bg-zinc-800 p-1 rounded-md print:hidden w-full"
|
||||
:placeholder="$t('train-search-placeholder')"
|
||||
/>
|
||||
|
||||
<div v-if="globalStore.viewMode == 'journal'" class="w-full relative">
|
||||
<input
|
||||
type="text"
|
||||
v-model="globalStore.journalTimetableSearch"
|
||||
class="bg-zinc-800 p-1 rounded-md print:hidden w-full"
|
||||
:placeholder="$t('journal-search-placeholder')"
|
||||
@keydown.enter="fetchJournalTrain"
|
||||
/>
|
||||
|
||||
<div class="absolute top-0 right-0">
|
||||
<button class="bg-zinc-800 p-1 rounded-md hover:bg-zinc-700">
|
||||
<MagnifyingGlassIcon class="text-white size-6" />
|
||||
</button>
|
||||
|
||||
<button class="bg-zinc-800 p-1 rounded-md hover:bg-zinc-700">
|
||||
<XMarkIcon class="text-white size-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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" />
|
||||
@@ -64,18 +125,31 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } 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,
|
||||
CloudArrowDownIcon,
|
||||
WifiIcon,
|
||||
XMarkIcon,
|
||||
MagnifyingGlassIcon,
|
||||
Bars3Icon,
|
||||
} 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();
|
||||
const globalStore = useGlobalStore();
|
||||
|
||||
const isMenuOpen = ref(false);
|
||||
|
||||
// Computed
|
||||
const isTimetableSaved = computed(() => {
|
||||
if (!globalStore.currentTimetableData) return false;
|
||||
@@ -94,8 +168,34 @@ function selectTrain() {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleViewMode() {
|
||||
globalStore.viewMode = globalStore.viewMode == 'active' ? 'storage' : 'active';
|
||||
function fetchJournalTrain(e: Event) {
|
||||
e.preventDefault();
|
||||
|
||||
console.log(globalStore.journalTimetableSearch);
|
||||
}
|
||||
|
||||
function toggleViewMode(viewMode: ViewMode) {
|
||||
if (viewMode == globalStore.viewMode) {
|
||||
switch (viewMode) {
|
||||
case 'active':
|
||||
globalStore.selectedActiveTrain = null;
|
||||
break;
|
||||
|
||||
case 'storage':
|
||||
globalStore.selectedStorageTimetable = null;
|
||||
break;
|
||||
|
||||
case 'journal':
|
||||
globalStore.selectedJournalTimetable = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
isMenuOpen.value = false;
|
||||
globalStore.viewMode = viewMode;
|
||||
}
|
||||
|
||||
function toggleDarkMode() {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<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 class="font-bold text-2xl p-1 bg-zinc-800">{{ $t('storage-empty-header') }}</div>
|
||||
<div class="text-md mt-2 p-2 bg-zinc-900">{{ $t('storage-empty-info') }}</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div class="font-bold text-xl p-2 bg-zinc-700 mb-3">{{ $t('storage-preview-title') }}</div>
|
||||
<div class="font-bold text-2xl p-2 bg-zinc-800 mb-3">{{ $t('storage-preview-title') }}</div>
|
||||
<div class="font-bold p-2 bg-zinc-800 mb-3" v-if="filteredTimetables.length == 0">{{ $t('storage-preview-empty') }}</div>
|
||||
|
||||
<li v-for="timetable in filteredTimetables" class="flex gap-1 w-full my-2">
|
||||
@@ -36,11 +36,11 @@ const i18n = useI18n();
|
||||
const filteredTimetables = computed(() => {
|
||||
let timetables = Object.values(globalStore.storageTimetables);
|
||||
|
||||
if (globalStore.timetableSearch.length != 0)
|
||||
if (globalStore.storageTimetableSearch.length != 0)
|
||||
timetables = timetables.filter((st) =>
|
||||
`${st.timetableId} ${st.driverName} ${st.route} ${st.category} ${st.trainNo}`
|
||||
.toLocaleLowerCase()
|
||||
.includes(globalStore.timetableSearch.toLocaleLowerCase())
|
||||
.includes(globalStore.storageTimetableSearch.toLocaleLowerCase())
|
||||
);
|
||||
|
||||
timetables.sort((a, b) => {
|
||||
|
||||
@@ -19,12 +19,11 @@
|
||||
|
||||
<div class="overflow-auto text-center font-bold text-zinc-400 p-1 min-h-full" v-else>
|
||||
<div v-if="globalStore.viewMode == 'active'">
|
||||
<div>{{ $t('train-select-info') }}</div>
|
||||
<div class="text-xl p-2 bg-zinc-900">{{ $t('train-select-info') }}</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<TimetableStorage />
|
||||
</div>
|
||||
<TimetableStorage v-else-if="globalStore.viewMode == 'storage'" />
|
||||
<TimetableJournal v-else-if="globalStore.viewMode == 'journal'" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -37,6 +36,7 @@ import TimetableBody from './TimetableBody.vue';
|
||||
import TimetableHeader from './TimetableHeader.vue';
|
||||
import type { SceneryRoute, StopRow, TimetablePathData } from '../../types/common.types';
|
||||
import TimetableStorage from './TimetableStorage.vue';
|
||||
import TimetableJournal from './TimetableJournal.vue';
|
||||
|
||||
const globalStore = useGlobalStore();
|
||||
const apiStore = useApiStore();
|
||||
|
||||
+2
-1
@@ -22,5 +22,6 @@
|
||||
"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?"
|
||||
"delete-timetable-confirm": "Are you sure that you want to delete this timetable?",
|
||||
"journal-search-placeholder": "Driver nickname"
|
||||
}
|
||||
+2
-1
@@ -22,5 +22,6 @@
|
||||
"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?"
|
||||
"delete-timetable-confirm": "Czy na pewno chcesz usunąć ten rozkład jazdy z archiwum?",
|
||||
"journal-search-placeholder": "Nick maszynisty"
|
||||
}
|
||||
+24
-3
@@ -1,8 +1,8 @@
|
||||
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 JournalTimetableShortResponse, type SceneriesDataResponse } from '../types/api.types';
|
||||
import type { ActiveData, JournalTimetableShort, SceneryData } from '../types/common.types';
|
||||
|
||||
export const useApiStore = defineStore('api', {
|
||||
state() {
|
||||
@@ -12,6 +12,8 @@ export const useApiStore = defineStore('api', {
|
||||
activeData: null as ActiveData | null,
|
||||
sceneryData: null as SceneryData[] | null,
|
||||
|
||||
journalTimetables: null as JournalTimetableShort[] | null,
|
||||
|
||||
outdatedTimerId: -1,
|
||||
isActiveDataOutdated: false,
|
||||
|
||||
@@ -43,7 +45,6 @@ export const useApiStore = defineStore('api', {
|
||||
this.fetchSceneriesData();
|
||||
await this.fetchActiveData();
|
||||
|
||||
|
||||
setInterval(() => {
|
||||
this.fetchActiveData();
|
||||
}, 25000);
|
||||
@@ -76,5 +77,25 @@ export const useApiStore = defineStore('api', {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
async fetchTimetableHistoryList() {
|
||||
try {
|
||||
const response = (
|
||||
await this.client!.get<JournalTimetableShortResponse>('/api/getTimetables', {
|
||||
params: {
|
||||
driverName: 'Spythere',
|
||||
returnType: 'short',
|
||||
hasStopsDetails: 1
|
||||
},
|
||||
})
|
||||
).data;
|
||||
|
||||
this.journalTimetables = response;
|
||||
|
||||
// this.sceneryData = response;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,6 +11,8 @@ export const useGlobalStore = defineStore('global', {
|
||||
selectedTrainId: null as string | null,
|
||||
selectedActiveTrain: null as ActiveTrain | null,
|
||||
selectedStorageTimetable: null as TimetableData | null,
|
||||
selectedJournalTimetable: null,
|
||||
|
||||
storageTimetables: {} as Record<number, TimetableData>,
|
||||
|
||||
timetableWarnings: [] as string[],
|
||||
@@ -18,7 +20,8 @@ export const useGlobalStore = defineStore('global', {
|
||||
generatedDate: null as Date | null,
|
||||
generatedMs: 0,
|
||||
|
||||
timetableSearch: '',
|
||||
storageTimetableSearch: '',
|
||||
journalTimetableSearch: '',
|
||||
|
||||
showSettings: false,
|
||||
}),
|
||||
@@ -52,7 +55,7 @@ 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 : ''};${
|
||||
@@ -70,10 +73,10 @@ export const useGlobalStore = defineStore('global', {
|
||||
return unitNameCorrections[unitName] ?? unitName;
|
||||
}),
|
||||
};
|
||||
} else {
|
||||
} else if (this.viewMode == 'storage') {
|
||||
const selectedStorageTimetable = this.selectedStorageTimetable;
|
||||
return selectedStorageTimetable;
|
||||
}
|
||||
} else return null;
|
||||
},
|
||||
},
|
||||
actions: {},
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import type { ActiveData, SceneryData } from './common.types';
|
||||
import type { ActiveData, JournalTimetableDetailed, JournalTimetableShort, SceneryData } from './common.types';
|
||||
|
||||
export type ActiveDataResponse = ActiveData;
|
||||
|
||||
export type SceneriesDataResponse = SceneryData[];
|
||||
|
||||
export type JournalTimetableShortResponse = JournalTimetableShort[];
|
||||
export type JournalTimetableDetailedResponse = JournalTimetableDetailed[];
|
||||
|
||||
export enum DataStatus {
|
||||
'LOADING' = 0,
|
||||
'SUCCESS' = 1,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type ViewMode = 'active' | 'storage';
|
||||
export type ViewMode = 'active' | 'storage' | 'journal';
|
||||
|
||||
export interface ActiveData {
|
||||
trains: ActiveTrain[];
|
||||
@@ -240,7 +240,7 @@ export interface JournalTimetableDetailed extends JournalTimetableShort {
|
||||
warningNotes: string;
|
||||
hasDangerousCargo: boolean;
|
||||
hasExtraDeliveries: boolean;
|
||||
stopListString: any;
|
||||
stopListString?: string;
|
||||
}
|
||||
|
||||
export interface TimetableData {
|
||||
|
||||
Reference in New Issue
Block a user