From c5221e337b490214c50597fce502c3be070d0e7e Mon Sep 17 00:00:00 2001 From: Spythere Date: Tue, 1 Jul 2025 16:12:44 +0200 Subject: [PATCH 01/14] chore: added multiline support --- src/components/OrderMessage.vue | 7 ++++++- src/components/OrderN.vue | 4 ++-- src/components/OrderO.vue | 22 +++++++++------------- src/components/OrderS.vue | 4 ++-- src/mixins/orderFooterMixin.ts | 6 +++--- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/components/OrderMessage.vue b/src/components/OrderMessage.vue index 17fc50b..7c17a55 100644 --- a/src/components/OrderMessage.vue +++ b/src/components/OrderMessage.vue @@ -2,7 +2,7 @@

Wiadomość do wyświetlenia na czacie symulatora:

-
+

Po wygenerowaniu rozkazu skopiuj jego treść lub zapisz w pamięci przeglądarki za pomocą przycisków poniżej @@ -116,6 +116,11 @@ export default defineComponent({ computed: { fullOrderMessage() { return this.store.orderMessage + this.store.footerMessage; + }, + + // Replace all new line tags with
for preview and get rid of the first one (visible only on simulator's chat) + orderMessagePreview() { + return this.fullOrderMessage.replace(/\n/g, '
').replace('
', ''); } }, diff --git a/src/components/OrderN.vue b/src/components/OrderN.vue index bb381cf..b37887e 100644 --- a/src/components/OrderN.vue +++ b/src/components/OrderN.vue @@ -350,7 +350,7 @@ export default defineComponent({ () => { const { header } = order; - const message = `Rozkaz pisemny "N" nr ${header.orderNo || '_'} dla pociągu nr ${ + const message = `\nRozkaz pisemny "N" nr ${header.orderNo || '_'} dla pociągu nr ${ header.trainNo || '_' } dnia ${header.date}`; @@ -508,7 +508,7 @@ export default defineComponent({ for (let i = 0; i < this.order.rows.length; i++) { if (!this.order.rows[i].enabled) continue; - message += ` [ ${i + 1} ] ${this.rowMethods[i + 1]()}`; + message += `\n--------\n[ ${i + 1} ] ${this.rowMethods[i + 1]()}`; } this.store.orderMessage = message; diff --git a/src/components/OrderO.vue b/src/components/OrderO.vue index 62ebf05..6c062bd 100644 --- a/src/components/OrderO.vue +++ b/src/components/OrderO.vue @@ -90,7 +90,7 @@ export default defineComponent({ () => { const { header } = order; - return `Rozkaz pisemny "O" nr ${header.orderNo || '_'} dla pociągu nr ${ + return `\nRozkaz pisemny "O" nr ${header.orderNo || '_'} dla pociągu nr ${ header.trainNo || '_' } dnia ${header.date || '_'}`; } @@ -120,28 +120,24 @@ export default defineComponent({ generateMessage() { let message = this.rowMethods[0](); - if (this.order.orderList.some((row) => row.name)) message += ` [ 1 ] `; + if (this.order.orderList.some((row) => row.name)) message += `\n--------\n[ 1 ]`; - const rowsMessageList = []; + message += '\n1) zmniejszyć prędkość jazdy i zachować ostrożność' + message += '\n2) jechać ostrożnie (j.o.)\n' for (let i = 0; i < this.order.orderList.length; i++) { const row = this.order.orderList[i]; if (!row.name) continue; - let rowMessage = ''; - rowMessage += ` ${row.name || '_'} od ${row.from || '_'} do ${row.to || '_'} kilometra`; + message += `\n- ${row.name || '_'} od ${row.from || '_'} do ${row.to || '_'} kilometra`; - if (row.vmax) rowMessage += ` prędkość najwyżej ${row.vmax} km/h`; - if (row.jo) rowMessage += ` jechać ostrożnie`; + if (row.vmax) message += ` prędkość najwyżej ${row.vmax} km/h`; + if (row.jo) message += ` jechać ostrożnie`; - rowMessage += ` z powodu: ${row.reason || '_'}`; - - rowsMessageList.push(rowMessage); + message += ` z powodu: ${row.reason || '_'}`; } - message += rowsMessageList.join('; '); - - if (this.order.other) message += ` [ 2 ] Inne: ${this.order.other}`; + if (this.order.other) message += `\n--------\n[ 2 ] Inne: ${this.order.other}`; this.store.orderMessage = message; } diff --git a/src/components/OrderS.vue b/src/components/OrderS.vue index 8ecdd23..733653d 100644 --- a/src/components/OrderS.vue +++ b/src/components/OrderS.vue @@ -300,7 +300,7 @@ export default defineComponent({ () => { const { header } = order; - return `Rozkaz pisemny "S" nr ${header.orderNo || '_'} dla ${header.for || '_'} nr ${ + return `\nRozkaz pisemny "S" nr ${header.orderNo || '_'} dla ${header.for || '_'} nr ${ header.trainNo || '_' } dnia ${header.date || '_'}`; }, @@ -429,7 +429,7 @@ export default defineComponent({ for (let i = 0; i < 4; i++) { if (!this.order.rows[i].enabled) continue; - message += ` [ ${i + 1} ] ${this.rowMethods[i + 1]()}`; + message += `\n--------\n[ ${i + 1} ] ${this.rowMethods[i + 1]()}`; } this.store.orderMessage = message; diff --git a/src/mixins/orderFooterMixin.ts b/src/mixins/orderFooterMixin.ts index 783a0a7..d788576 100644 --- a/src/mixins/orderFooterMixin.ts +++ b/src/mixins/orderFooterMixin.ts @@ -14,7 +14,7 @@ export default defineComponent({ const messageArray = []; - if (footer.stationName) messageArray.push(`stacja: ${footer.stationName}`); + messageArray.push(`stacja: ${footer.stationName ?? ''}`); if (footer.checkpointName) messageArray.push(`posterunek: ${footer.checkpointName}`); if (footer.hour) messageArray.push(`godz. ${footer.hour}`); if (footer.minutes) messageArray.push(`min. ${footer.minutes}`); @@ -22,9 +22,9 @@ export default defineComponent({ if (footer.secondaryDispatcherName) messageArray.push(`z polecenia dyżurnego ruchu ${footer.secondaryDispatcherName}`); - this.store.footerMessage = ` | ${messageArray.join( + this.store.footerMessage = `\n--------\n${messageArray.join( ', ' - )} | Rozkaz otrzymałem, maszynista: (potwierdzić otrzymanie rozkazu)`; + )}\n--------\nRozkaz otrzymałem, maszynista: (potwierdzić otrzymanie rozkazu)`; } } }); From 519665697b3cad0d408ead17a965134838428699 Mon Sep 17 00:00:00 2001 From: Spythere Date: Tue, 1 Jul 2025 16:15:50 +0200 Subject: [PATCH 02/14] chore: added i18n --- package.json | 2 +- src/i18n.ts | 19 +++++++++++++++++++ src/locales/en.json | 1 + src/locales/pl.json | 1 + src/main.ts | 4 +++- yarn.lock | 42 +++++++++++++++++++++--------------------- 6 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 src/i18n.ts create mode 100644 src/locales/en.json create mode 100644 src/locales/pl.json diff --git a/package.json b/package.json index f1005cb..477b691 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "axios": "^1.6.2", "pinia": "^2.1.7", "vue": "^3.3.11", - "vue-i18n": "9.8.0", + "vue-i18n": "11", "vue-router": "^4.2.5", "vue-tsc": "^2.2.8" }, diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 0000000..bf1b9b2 --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,19 @@ +import enLang from './locales/en.json'; +import plLang from './locales/pl.json'; + +import { createI18n } from 'vue-i18n'; + +const i18n = createI18n({ + locale: 'pl', + legacy: false, + warnHtmlMessage: false, + fallbackLocale: 'pl', + + messages: { + en: enLang, + pl: plLang + }, + enableLegacy: false +}); + +export default i18n; diff --git a/src/locales/en.json b/src/locales/en.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/locales/en.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/locales/pl.json b/src/locales/pl.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/locales/pl.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 0cada51..4d4ea46 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,4 +3,6 @@ import App from './App.vue'; import router from './router'; import { createPinia } from 'pinia'; -createApp(App).use(router).use(createPinia()).mount('#app'); +import i18n from './i18n'; + +createApp(App).use(router).use(i18n).use(createPinia()).mount('#app'); diff --git a/yarn.lock b/yarn.lock index e260818..c9f1a1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -963,26 +963,26 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== -"@intlify/core-base@9.8.0": - version "9.8.0" - resolved "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.8.0.tgz" - integrity sha512-UxaSZVZ1DwqC/CltUZrWZNaWNhfmKtfyV4BJSt/Zt4Or/fZs1iFj0B+OekYk1+MRHfIOe3+x00uXGQI4PbO/9g== +"@intlify/core-base@11.1.7": + version "11.1.7" + resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-11.1.7.tgz#497280e4774011cf0d42eaedb20e9cd4594c0a3f" + integrity sha512-gYiGnQeJVp3kNBeXQ73m1uFOak0ry4av8pn+IkEWigyyPWEMGzB+xFeQdmGMFn49V+oox6294oGVff8bYOhtOw== dependencies: - "@intlify/message-compiler" "9.8.0" - "@intlify/shared" "9.8.0" + "@intlify/message-compiler" "11.1.7" + "@intlify/shared" "11.1.7" -"@intlify/message-compiler@9.8.0": - version "9.8.0" - resolved "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.8.0.tgz" - integrity sha512-McnYWhcoYmDJvssVu6QGR0shqlkJuL1HHdi5lK7fNqvQqRYaQ4lSLjYmZxwc8tRNMdIe9/KUKfyPxU9M6yCtNQ== +"@intlify/message-compiler@11.1.7": + version "11.1.7" + resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-11.1.7.tgz#047ba659cfd34b0f630dddf73c3f9224bd3af7f8" + integrity sha512-0ezkep1AT30NyuKj8QbRlmvMORCCRlOIIu9v8RNU8SwDjjTiFCZzczCORMns2mCH4HZ1nXgrfkKzYUbfjNRmng== dependencies: - "@intlify/shared" "9.8.0" + "@intlify/shared" "11.1.7" source-map-js "^1.0.2" -"@intlify/shared@9.8.0": - version "9.8.0" - resolved "https://registry.npmjs.org/@intlify/shared/-/shared-9.8.0.tgz" - integrity sha512-TmgR0RCLjzrSo+W3wT0ALf9851iFMlVI9EYNGeWvZFUQTAJx0bvfsMlPdgVtV1tDNRiAfhkFsMKu6jtUY1ZLKQ== +"@intlify/shared@11.1.7": + version "11.1.7" + resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-11.1.7.tgz#54e60d52b73fb25019e2689d6531a54928b40194" + integrity sha512-4yZeMt2Aa/7n5Ehy4KalUlvt3iRLcg1tq9IBVfOgkyWFArN4oygn6WxgGIFibP3svpaH8DarbNaottq+p0gUZQ== "@jridgewell/gen-mapping@^0.3.5": version "0.3.8" @@ -4172,13 +4172,13 @@ vue-eslint-parser@^9.3.1, vue-eslint-parser@^9.4.3: lodash "^4.17.21" semver "^7.3.6" -vue-i18n@9.8.0: - version "9.8.0" - resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.8.0.tgz" - integrity sha512-Izho+6PYjejsTq2mzjcRdBZ5VLRQoSuuexvR8029h5CpN03FYqiqBrShMyf2I1DKkN6kw/xmujcbvC+4QybpsQ== +vue-i18n@11: + version "11.1.7" + resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-11.1.7.tgz#a26c0224d1311ac89b82ff6d0ee45f68b5099237" + integrity sha512-CDrU7Cmyh1AxJjerQmipV9nVa//exVBdhTcWGlbfcDCN8bKp/uAe7Le6IoN4//5emIikbsSKe9Uofmf/xXkhOA== dependencies: - "@intlify/core-base" "9.8.0" - "@intlify/shared" "9.8.0" + "@intlify/core-base" "11.1.7" + "@intlify/shared" "11.1.7" "@vue/devtools-api" "^6.5.0" vue-router@^4.2.5: From e298a17ab7418f900813e983564c6694d879bc0c Mon Sep 17 00:00:00 2001 From: Spythere Date: Tue, 1 Jul 2025 16:20:25 +0200 Subject: [PATCH 03/14] chore: added loading locale from browser data --- src/App.vue | 52 +++++++++++++++++++++++++++++---- src/managers/storageManager.ts | 53 ++++++++++++++++++++++++++++++++++ src/store/store.ts | 2 ++ 3 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 src/managers/storageManager.ts diff --git a/src/App.vue b/src/App.vue index d5399c5..2405202 100644 --- a/src/App.vue +++ b/src/App.vue @@ -22,6 +22,7 @@ import { defineComponent } from 'vue'; import packageInfo from '../package.json'; import { useStore } from './store/store'; import orderStorageMixin from './mixins/orderStorageMixin'; +import StorageManager from './managers/storageManager'; export default defineComponent({ mixins: [orderStorageMixin], @@ -31,20 +32,59 @@ export default defineComponent({ return { offlineReady, needRefresh, updateServiceWorker }; }, + data() { return { appVersion: packageInfo.version, store: useStore() }; }, created() { - document.title = `GeneraTOR ${this.appVersion}`; - this.store.orderDarkMode = this.getOrderSetting('dark-mode') === 'true'; + this.init(); + }, - const query = new URLSearchParams(window.location.search); + methods: { + init() { + this.loadLang(); + this.loadSettings(); + this.handleQueries(); + }, - const id = query.get('sceneryId'); + loadSettings() { + document.title = `GeneraTOR ${this.appVersion}`; + this.store.orderDarkMode = this.getOrderSetting('dark-mode') === 'true'; + }, - if (id != null) { - this.store.orderMode = 'OrderTrainPicker'; + handleQueries() { + const query = new URLSearchParams(window.location.search); + + const id = query.get('sceneryId'); + + if (id != null) { + this.store.orderMode = 'OrderTrainPicker'; + } + }, + + changeLang(lang: string) { + this.$i18n.locale = lang; + this.store.currentAppLocale = lang; + + StorageManager.setStringValue('lang', lang); + }, + + loadLang() { + const storageLang = StorageManager.getStringValue('lang'); + + if (storageLang) { + this.changeLang(storageLang); + return; + } + + if (!window.navigator.language) return; + + const naviLanguage = window.navigator.language.toString(); + + if (!naviLanguage.startsWith('pl')) { + this.changeLang('en'); + } } } }); diff --git a/src/managers/storageManager.ts b/src/managers/storageManager.ts new file mode 100644 index 0000000..5675524 --- /dev/null +++ b/src/managers/storageManager.ts @@ -0,0 +1,53 @@ +export default class StorageManager { + static registerStorage(name: string) { + window.localStorage.setItem(name, '1'); + } + + static unregisterStorage(name: string) { + window.localStorage.removeItem(name); + } + + static isRegistered(name: string) { + return window.localStorage.getItem(name) ? true : false; + } + + static setBooleanValue(key: string, val: boolean) { + window.localStorage.setItem(key, val.toString()); + } + + static setNumericValue(key: string, val: number) { + window.localStorage.setItem(key, val.toString()); + } + + static setStringValue(key: string, val: string) { + window.localStorage.setItem(key, val); + } + + static setValue(key: string, val: any) { + if (typeof val == 'boolean') this.setBooleanValue(key, val); + else if (typeof val == 'number') this.setNumericValue(key, val); + else if (typeof val == 'string') this.setStringValue(key, val); + else this.setStringValue(key, val); + } + + static removeValue(key: string) { + window.localStorage.removeItem(key); + } + + static getValue(key: string) { + return window.localStorage.getItem(key); + } + + static getBooleanValue(key: string): boolean { + return window.localStorage.getItem(key) === 'true' ? true : false; + } + + static getStringValue(key: string): string { + return window.localStorage.getItem(key) || ''; + } + + static getNumericValue(key: string): number { + const itemValue = window.localStorage.getItem(key); + return itemValue ? parseInt(itemValue) : 0; + } +} diff --git a/src/store/store.ts b/src/store/store.ts index cdbf74c..cbe4cc2 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -9,6 +9,8 @@ import { export const useStore = defineStore('store', { state: () => { return { + currentAppLocale: 'pl', + helperModalOpen: false, orderDarkMode: false, From 8538072a6084e5ca4446ada899bed0291f7cc119 Mon Sep 17 00:00:00 2001 From: Spythere Date: Tue, 1 Jul 2025 18:17:39 +0200 Subject: [PATCH 04/14] refactor: added translations --- package.json | 1 + src/components/OrderList.vue | 25 +++++++---- src/components/OrderMessage.vue | 51 +++++++++++------------ src/components/OrderTrainPicker.vue | 28 ++++++++----- src/locales/en.json | 64 ++++++++++++++++++++++++++++- src/locales/pl.json | 64 ++++++++++++++++++++++++++++- src/mixins/orderValidationMixin.ts | 2 +- src/styles/_global.scss | 6 +++ src/views/Home.vue | 39 ++++++++++++++---- yarn.lock | 5 +++ 10 files changed, 229 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index 477b691..1b5ebef 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "axios": "^1.6.2", + "lucide-vue-next": "^0.525.0", "pinia": "^2.1.7", "vue": "^3.3.11", "vue-i18n": "11", diff --git a/src/components/OrderList.vue b/src/components/OrderList.vue index 45acee4..13166f9 100644 --- a/src/components/OrderList.vue +++ b/src/components/OrderList.vue @@ -1,10 +1,10 @@