Compare commits

...

10 Commits
1.5.0 ... 1.5.1

11 changed files with 3114 additions and 3346 deletions

View File

@@ -2,9 +2,24 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC. All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [1.5.1](https://git.odit.services/lfk/frontend/compare/1.5.0...1.5.1)
- chore(deps): pnpm@9 [`35bec9f`](https://git.odit.services/lfk/frontend/commit/35bec9fe584b93cd52e8bab4e469713468a67f70)
- chore(deps): bump some [`8fae1fb`](https://git.odit.services/lfk/frontend/commit/8fae1fb6b3e033f789d2568cbd2640c0d163dc53)
- fix(scanstations): CopyScanStationTokenModal open after create [`372fa11`](https://git.odit.services/lfk/frontend/commit/372fa110ec402dae166a302f2209c79353983148)
- fix(config): add explicit window.config [`91d2f46`](https://git.odit.services/lfk/frontend/commit/91d2f46b934bcba1429bd1d96e772c25c42a3e28)
- fix(dockerfile): AS casing [`50e81a6`](https://git.odit.services/lfk/frontend/commit/50e81a6cb5773381e153cbec3bed7db820ced84a)
- refactor(scanstations/CopyScanStationTokenModal): drop dispatch [`a5e72a1`](https://git.odit.services/lfk/frontend/commit/a5e72a18e368b5a7ee7b4e1894de613ecb767f28)
- chore(deps): node:23.2.0 [`93d67bd`](https://git.odit.services/lfk/frontend/commit/93d67bdba90a67b45d8895d9facaf66e908d53d6)
- fix(tracks/AddTrackModal): i18n [`c60bae4`](https://git.odit.services/lfk/frontend/commit/c60bae45334c2aa90d8931da07691c196469da46)
- fix: tailwind config [`43ac878`](https://git.odit.services/lfk/frontend/commit/43ac878d44b556c6d7811610f6fe0c9a5eff305f)
#### [1.5.0](https://git.odit.services/lfk/frontend/compare/1.4.13...1.5.0) #### [1.5.0](https://git.odit.services/lfk/frontend/compare/1.4.13...1.5.0)
> 20 November 2024
- feat(ci)!: Switch to woodpecker [`fb8206f`](https://git.odit.services/lfk/frontend/commit/fb8206ff130f4f65dcf619a2a786e7d5895b77a1) - feat(ci)!: Switch to woodpecker [`fb8206f`](https://git.odit.services/lfk/frontend/commit/fb8206ff130f4f65dcf619a2a786e7d5895b77a1)
- 🚀RELEASE v1.5.0 [`ceabd06`](https://git.odit.services/lfk/frontend/commit/ceabd06a4319c3c9ffab680f909730d5bd789540)
- fix(components): Add missing toast imports [`9bfc0c5`](https://git.odit.services/lfk/frontend/commit/9bfc0c5338933e832d5df50457c7978c026d8df6) - fix(components): Add missing toast imports [`9bfc0c5`](https://git.odit.services/lfk/frontend/commit/9bfc0c5338933e832d5df50457c7978c026d8df6)
#### [1.4.13](https://git.odit.services/lfk/frontend/compare/1.4.12...1.4.13) #### [1.4.13](https://git.odit.services/lfk/frontend/compare/1.4.12...1.4.13)

View File

@@ -1,9 +1,9 @@
FROM registry.odit.services/hub/library/node:20.0.0-alpine3.17 as build FROM registry.odit.services/hub/library/node:23.2.0-alpine3.20 AS build
ARG NPM_REGISTRY_URL=https://registry.npmjs.org ARG NPM_REGISTRY_URL=https://registry.npmjs.org
WORKDIR /app WORKDIR /app
COPY package.json pnpm-lock.yaml vite.config.js tailwind.config.js postcss.config.cjs index.html ./ COPY package.json pnpm-lock.yaml vite.config.js tailwind.config.cjs postcss.config.cjs index.html ./
RUN npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@8 RUN npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@9
RUN mkdir /pnpm && pnpm config set store-dir /pnpm && pnpm i RUN mkdir /pnpm && pnpm config set store-dir /pnpm && pnpm i
COPY src ./src COPY src ./src
@@ -11,6 +11,6 @@ COPY public ./public
RUN pnpm build RUN pnpm build
# final image # final image
FROM registry.odit.services/library/nginx-brotli:3.15 as final FROM registry.odit.services/library/nginx-brotli:3.15 AS final
COPY --from=build /app/dist /usr/share/nginx/html COPY --from=build /app/dist /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/nginx.conf COPY ./nginx.conf /etc/nginx/nginx.conf

View File

@@ -13,7 +13,7 @@
<body> <body>
<span style="display: none; visibility: hidden" id="buildinfo" <span style="display: none; visibility: hidden" id="buildinfo"
>RELEASE_INFO-1.5.0-RELEASE_INFO</span >RELEASE_INFO-1.5.1-RELEASE_INFO</span
> >
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<script src="/env.js"></script> <script src="/env.js"></script>

View File

@@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-frontend", "name": "@odit/lfk-frontend",
"version": "1.5.0", "version": "1.5.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"i18n-order": "node order.js", "i18n-order": "node order.js",
@@ -14,14 +14,14 @@
"devDependencies": { "devDependencies": {
"@odit/license-exporter": "0.2.0", "@odit/license-exporter": "0.2.0",
"@sveltejs/vite-plugin-svelte": "2.1.1", "@sveltejs/vite-plugin-svelte": "2.1.1",
"auto-changelog": "2.4.0", "auto-changelog": "2.5.0",
"autoprefixer": "10.4.14", "autoprefixer": "10.4.20",
"postcss": "8.4.23", "postcss": "8.4.49",
"prettier": "2.8.8", "prettier": "3.3.3",
"prettier-plugin-svelte": "2.10.0", "prettier-plugin-svelte": "3.2.8",
"release-it": "15.10.3", "release-it": "17.10.0",
"svelte-select": "3.17.0", "svelte-select": "3.17.0",
"tailwindcss": "3.3.2", "tailwindcss": "3.4.15",
"vite": "4.3.3" "vite": "4.3.3"
}, },
"release-it": { "release-it": {
@@ -42,11 +42,11 @@
} }
}, },
"dependencies": { "dependencies": {
"@odit/lfk-client-js": "1.1.2", "@odit/lfk-client-js": "1.1.3",
"@paralleldrive/cuid2": "2.2.0", "@paralleldrive/cuid2": "2.2.2",
"@tanstack/svelte-table": "8.9.1", "@tanstack/svelte-table": "8.9.1",
"bwip-js": "3.4.0", "bwip-js": "3.4.0",
"check-password-strength": "2.0.7", "check-password-strength": "2.0.10",
"csvtojson": "2.0.10", "csvtojson": "2.0.10",
"localforage": "1.10.0", "localforage": "1.10.0",
"marked": "4.3.0", "marked": "4.3.0",

5447
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,3 +8,4 @@ const config = {
default_password: "demo", default_password: "demo",
prefersHashRouting: true, prefersHashRouting: true,
}; };
window.config = config;

View File

@@ -1,12 +1,11 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { tick } from "svelte";
import { tick, createEventDispatcher } from "svelte";
import bwipjs from "bwip-js"; import bwipjs from "bwip-js";
import toast from "svelte-french-toast";
export let copy_modal_open; export let copy_modal_open;
export let new_station; export let new_station;
const dispatch = createEventDispatcher();
let valueCopy = null; let valueCopy = null;
let areaDom; let areaDom;
let copied = false; let copied = false;

View File

@@ -1,12 +1,25 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import store from "../../store"; import store from "../../store";
import { ScanStationService } from "@odit/lfk-client-js";
import AddScanStationModal from "./AddScanStationModal.svelte"; import AddScanStationModal from "./AddScanStationModal.svelte";
import CopyScanStationTokenModal from "./CopyScanStationTokenModal.svelte"; import CopyScanStationTokenModal from "./CopyScanStationTokenModal.svelte";
import ScanStationsOverview from "./ScanStationsOverview.svelte"; import ScanStationsEmptyState from "./ScanStationsEmptyState.svelte";
import ConfirmScanStationDeletion from "./ConfirmScanStationDeletion.svelte";
import toast from "svelte-french-toast";
//
export let modal_open = false; export let modal_open = false;
export let copy_modal_open = false; export let copy_modal_open = false;
export let new_station = {}; export let new_station = {};
//
const promise = ScanStationService.scanStationControllerGetAll().then(
(result) => {
current_stations = result;
}
);
$: searchvalue = "";
$: active_deletes = [];
let delete_station = {};
let current_stations = []; let current_stations = [];
</script> </script>
@@ -25,12 +38,184 @@
</button> </button>
{/if} {/if}
</span> </span>
<ScanStationsOverview <ConfirmScanStationDeletion
bind:current_stations on:cancelDelete={(event) => {
modal_open = false;
active_deletes[event.detail.id] = false;
}}
bind:modal_open bind:modal_open
bind:new_station bind:delete_station
bind:copy_modal_open
/> />
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:GET")}
{#await promise}
<div
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
role="alert"
>
<p class="font-bold">{$_("scanstations-are-being-loaded")}</p>
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
</div>
{:then}
{#if current_stations.length === 0}
<ScanStationsEmptyState />
{:else}
<input
type="search"
bind:value={searchvalue}
placeholder={$_("datatable.search")}
aria-label={$_("datatable.search")}
class="mb-4"
/>
<div
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
>
<table class="divide-y divide-gray-200 w-full">
<thead class="bg-gray-50">
<tr class="odd:bg-white even:bg-gray-100">
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("track")}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("description")}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("status")}
</th>
<th scope="col" class="relative px-6 py-3">
<span class="sr-only">{$_("action")}</span>
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
{#each current_stations as s}
{#if Object.values(s)
.toString()
.toLowerCase()
.includes(searchvalue)}
<tr
class="odd:bg-white even:bg-gray-100"
data-rowid="station_{s.id}"
>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">
<a
href="../tracks"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
>
{s.track.name || s.track.distance + "m"}</a
>
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">
{s.description}
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
{#if s.enabled}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
>{$_("active")}</span
>
{:else}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800"
>{$_("inactive")}</span
>
{/if}
</div>
</td>
{#if active_deletes[s.id] === true}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<button
on:click={() => {
active_deletes[s.id] = false;
}}
tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer"
>{$_("cancel-delete")}</button
>
<button
on:click={() => {
ScanStationService.scanStationControllerRemove(
s.id,
false
)
.then((resp) => {
current_stations = current_stations.filter(
(obj) => obj.id !== s.id
);
toast($_("station-deleted"));
})
.catch((err) => {
modal_open = true;
delete_station = s;
});
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("confirm-delete")}</button
>
</td>
{:else}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<a
href="/scanstations/{s.id}"
class="text-indigo-600 hover:text-indigo-900"
>{$_("details")}</a
>
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:DELETE")}
<button
on:click={() => {
active_deletes[s.id] = true;
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("delete")}</button
>
{/if}
</td>
{/if}
</tr>
{/if}
{/each}
</tbody>
</table>
</div>
{/if}
{:catch error}
<div
class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"
>
<span class="inline-block align-middle mr-8">
<b class="capitalize">{$_("general_promise_error")}</b>
{error}
</span>
</div>
{/await}
{/if}
</section> </section>
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:CREATE")} {#if store.state.jwtinfo.userdetails.permissions.includes("STATION:CREATE")}

View File

@@ -1,195 +0,0 @@
<script>
import { _ } from "svelte-i18n";
import { ScanStationService } from "@odit/lfk-client-js";
const promise = ScanStationService.scanStationControllerGetAll().then(
(result) => {
current_stations = result;
}
);
import store from "../../store";
import ScanStationsEmptyState from "./ScanStationsEmptyState.svelte";
import ConfirmScanStationDeletion from "./ConfirmScanStationDeletion.svelte";
import toast from "svelte-french-toast";
$: searchvalue = "";
$: active_deletes = [];
let delete_station = {};
let modal_open = false;
export let current_stations = [];
</script>
<ConfirmScanStationDeletion
on:cancelDelete={(event) => {
modal_open = false;
active_deletes[event.detail.id] = false;
}}
bind:modal_open
bind:delete_station
/>
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:GET")}
{#await promise}
<div
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
role="alert"
>
<p class="font-bold">{$_("scanstations-are-being-loaded")}</p>
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
</div>
{:then}
{#if current_stations.length === 0}
<ScanStationsEmptyState />
{:else}
<input
type="search"
bind:value={searchvalue}
placeholder={$_("datatable.search")}
aria-label={$_("datatable.search")}
class="mb-4"
/>
<div
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
>
<table class="divide-y divide-gray-200 w-full">
<thead class="bg-gray-50">
<tr class="odd:bg-white even:bg-gray-100">
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("track")}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("description")}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("status")}
</th>
<th scope="col" class="relative px-6 py-3">
<span class="sr-only">{$_("action")}</span>
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
{#each current_stations as s}
{#if Object.values(s)
.toString()
.toLowerCase()
.includes(searchvalue)}
<tr
class="odd:bg-white even:bg-gray-100"
data-rowid="station_{s.id}"
>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">
<a
href="../tracks"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
>
{s.track.name || s.track.distance + "m"}</a
>
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">
{s.description}
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
{#if s.enabled}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
>{$_("active")}</span
>
{:else}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800"
>{$_("inactive")}</span
>
{/if}
</div>
</td>
{#if active_deletes[s.id] === true}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<button
on:click={() => {
active_deletes[s.id] = false;
}}
tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer"
>{$_("cancel-delete")}</button
>
<button
on:click={() => {
ScanStationService.scanStationControllerRemove(
s.id,
false
)
.then((resp) => {
current_stations = current_stations.filter(
(obj) => obj.id !== s.id
);
toast($_("station-deleted"));
})
.catch((err) => {
modal_open = true;
delete_station = s;
});
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("confirm-delete")}</button
>
</td>
{:else}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<a
href="/scanstations/{s.id}"
class="text-indigo-600 hover:text-indigo-900"
>{$_("details")}</a
>
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:DELETE")}
<button
on:click={() => {
active_deletes[s.id] = true;
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("delete")}</button
>
{/if}
</td>
{/if}
</tr>
{/if}
{/each}
</tbody>
</table>
</div>
{/if}
{:catch error}
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
<span class="inline-block align-middle mr-8">
<b class="capitalize">{$_("general_promise_error")}</b>
{error}
</span>
</div>
{/await}
{/if}

View File

@@ -223,7 +223,7 @@
type="button" type="button"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"
> >
Create {$_("create")}
</button> </button>
<button <button
on:click={() => { on:click={() => {