Begin Display, Add View Transitions
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful

Dieser Commit ist enthalten in:
Chaoscaot 2024-02-28 17:28:21 +01:00
Ursprung 361d7dae6a
Commit 678746c89b
31 geänderte Dateien mit 349 neuen und 212 gelöschten Zeilen

Datei anzeigen

@ -56,6 +56,7 @@
"flowbite": "^1.8.1", "flowbite": "^1.8.1",
"flowbite-svelte": "^0.44.24", "flowbite-svelte": "^0.44.24",
"flowbite-svelte-icons": "^0.4.5", "flowbite-svelte-icons": "^0.4.5",
"qs": "^6.11.2",
"sharp": "^0.32.6", "sharp": "^0.32.6",
"svelte-awesome": "^3.3.1", "svelte-awesome": "^3.3.1",
"svelte-codemirror-editor": "^1.2.0", "svelte-codemirror-editor": "^1.2.0",

Datei anzeigen

@ -56,6 +56,9 @@ dependencies:
flowbite-svelte-icons: flowbite-svelte-icons:
specifier: ^0.4.5 specifier: ^0.4.5
version: 0.4.5(svelte@4.2.12)(tailwind-merge@2.2.1)(tailwindcss@3.4.1) version: 0.4.5(svelte@4.2.12)(tailwind-merge@2.2.1)(tailwindcss@3.4.1)
qs:
specifier: ^6.11.2
version: 6.11.2
sharp: sharp:
specifier: ^0.32.6 specifier: ^0.32.6
version: 0.32.6 version: 0.32.6
@ -1985,7 +1988,6 @@ packages:
function-bind: 1.1.2 function-bind: 1.1.2
get-intrinsic: 1.2.4 get-intrinsic: 1.2.4
set-function-length: 1.2.1 set-function-length: 1.2.1
dev: true
/callsites@3.1.0: /callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
@ -2400,7 +2402,6 @@ packages:
es-define-property: 1.0.0 es-define-property: 1.0.0
es-errors: 1.3.0 es-errors: 1.3.0
gopd: 1.0.1 gopd: 1.0.1
dev: true
/define-properties@1.2.1: /define-properties@1.2.1:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
@ -2581,12 +2582,10 @@ packages:
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
get-intrinsic: 1.2.4 get-intrinsic: 1.2.4
dev: true
/es-errors@1.3.0: /es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: true
/es-iterator-helpers@1.0.17: /es-iterator-helpers@1.0.17:
resolution: {integrity: sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==} resolution: {integrity: sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==}
@ -3135,7 +3134,6 @@ packages:
has-proto: 1.0.3 has-proto: 1.0.3
has-symbols: 1.0.3 has-symbols: 1.0.3
hasown: 2.0.1 hasown: 2.0.1
dev: true
/get-stream@8.0.1: /get-stream@8.0.1:
resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
@ -3224,7 +3222,6 @@ packages:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
dependencies: dependencies:
get-intrinsic: 1.2.4 get-intrinsic: 1.2.4
dev: true
/graceful-fs@4.2.11: /graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@ -3259,17 +3256,14 @@ packages:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
dependencies: dependencies:
es-define-property: 1.0.0 es-define-property: 1.0.0
dev: true
/has-proto@1.0.3: /has-proto@1.0.3:
resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: true
/has-symbols@1.0.3: /has-symbols@1.0.3:
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: true
/has-tostringtag@1.0.2: /has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
@ -4592,7 +4586,6 @@ packages:
/object-inspect@1.13.1: /object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
dev: true
/object-keys@1.1.1: /object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
@ -5287,6 +5280,13 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
/qs@6.11.2:
resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==}
engines: {node: '>=0.6'}
dependencies:
side-channel: 1.0.5
dev: false
/queue-microtask@1.2.3: /queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@ -5598,7 +5598,6 @@ packages:
get-intrinsic: 1.2.4 get-intrinsic: 1.2.4
gopd: 1.0.1 gopd: 1.0.1
has-property-descriptors: 1.0.2 has-property-descriptors: 1.0.2
dev: true
/set-function-name@2.0.2: /set-function-name@2.0.2:
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
@ -5650,7 +5649,6 @@ packages:
es-errors: 1.3.0 es-errors: 1.3.0
get-intrinsic: 1.2.4 get-intrinsic: 1.2.4
object-inspect: 1.13.1 object-inspect: 1.13.1
dev: true
/signal-exit@3.0.7: /signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}

Datei anzeigen

@ -21,6 +21,7 @@
import {l} from "@utils/util.ts"; import {l} from "@utils/util.ts";
import {t} from "astro-i18n"; import {t} from "astro-i18n";
import {get} from "svelte/store"; import {get} from "svelte/store";
import {navigate} from "astro:transitions/client";
let username: string = ""; let username: string = "";
let pw: string = ""; let pw: string = "";
@ -45,7 +46,7 @@
} }
tokenStore.set(auth); tokenStore.set(auth);
window.location.href = l("/dashboard"); navigate(l("/dashboard"));
} catch (e: any) { } catch (e: any) {
pw = ""; pw = "";
error = t("login.error"); error = t("login.error");

Datei anzeigen

@ -105,7 +105,7 @@
</div> </div>
</div> </div>
--> -->
<a class="btn my-1" href={l("/login")}> <a class="btn my-1" href={l("/login")} data-astro-reload>
<span class="btn__text">{t("navbar.links.account")}</span> <span class="btn__text">{t("navbar.links.account")}</span>
</a> </a>
</div> </div>

Datei anzeigen

@ -46,6 +46,10 @@
asyncComponent: () => import("./pages/Edit.svelte"), asyncComponent: () => import("./pages/Edit.svelte"),
conditions: detail => get(tokenStore) != "" conditions: detail => get(tokenStore) != ""
}), }),
"/display/:event": wrap({
asyncComponent: () => import("./pages/Display.svelte"),
conditions: detail => get(tokenStore) != ""
}),
"*": wrap({asyncComponent: () => import("./pages/NotFound.svelte")}) "*": wrap({asyncComponent: () => import("./pages/NotFound.svelte")})
}; };

Datei anzeigen

@ -0,0 +1,43 @@
<!--
- This file is a part of the SteamWar software.
-
- Copyright (C) 2024 SteamWar.de-Serverteam
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<script lang="ts">
import {eventRepo} from "@repo/event.ts";
import EventDisplay from "@components/admin/pages/display/EventDisplay.svelte";
export let params: { event: number };
let eventFuture = getEvent();
function getEvent() {
return $eventRepo.getEvent(params.event)
}
function refreshEvent() {
eventFuture = getEvent();
}
</script>
{#await eventFuture}
<p>Loading...</p>
{:then event}
<EventDisplay {event} />
{:catch error}
<p>{error.message}</p>
{/await}

Datei anzeigen

@ -139,6 +139,10 @@
<ul> <ul>
{#if (selectedPath)} {#if (selectedPath)}
{@const value = pagesMap.get(selectedPath) || []} {@const value = pagesMap.get(selectedPath) || []}
{#if value.length === 0}
<li class="p-4">No pages found</li>
<li class="p-4">Select a path on the top</li>
{/if}
{#each value as page} {#each value as page}
{@const nameRegexExec = nameRegex.exec(page.path)} {@const nameRegexExec = nameRegex.exec(page.path)}
{@const match = nameRegexExec ? nameRegexExec[0] : ""} {@const match = nameRegexExec ? nameRegexExec[0] : ""}

Datei anzeigen

@ -0,0 +1,103 @@
<!--
- This file is a part of the SteamWar software.
-
- Copyright (C) 2024 SteamWar.de-Serverteam
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<script lang="ts">
import type {ExtendedEvent} from "@type/event.ts";
import {onMount} from "svelte";
export let event: ExtendedEvent;
export let group: string | null = null;
const scrollTime = 10000;
$: groupFights = event.fights.filter(fight => group === null || fight.group === group);
let clock = new Date();
let scrollContainer: HTMLDivElement;
let scroll = 0;
onMount(() => {
const interval = setInterval(() => {
clock = new Date();
}, 1000);
const scrollinterval = setInterval(() => {
if (scroll >= scrollTime / 10) {
if (scrollContainer.scrollHeight - scrollContainer.scrollTop === scrollContainer.clientHeight) {
scrollContainer.scrollTo({
top: 0,
behavior: "smooth"
})
} else {
scrollContainer.scrollBy({
top: scrollContainer.clientHeight,
behavior: "smooth"
})
}
scroll = 0;
} else {
scroll += 1;
}
}, 10);
return () => {
clearInterval(interval);
clearInterval(scrollinterval);
};
})
</script>
<div class="bg-black text-white h-screen w-screen p-4 flex flex-col">
<div class="w-full border rounded-xl flex flex-col">
<div class="flex justify-between p-2">
<h1 class="self-center">{event.event.name}</h1>
<h1 class="self-start">{new Intl.DateTimeFormat("de-DE", {hour: "2-digit", minute: "2-digit"}).format(clock)}</h1>
</div>
<div role="progressbar" class="w-full h-2 rounded-b-full overflow-hidden border-t">
<div class="h-full bg-white" style="width: {scroll / (scrollTime / 1000)}%;"></div>
</div>
</div>
<div class="flex mt-4 flex-col gap-96 overflow-y-scroll overflow-x-hidden" bind:this={scrollContainer}>
{#each groupFights as fight (fight.id)}
<div class="flex justify-between border rounded-xl p-2 mb-96">
<h1>{fight.blueTeam.name} vs {fight.redTeam.name}</h1>
{#if fight.ergebnis !== 0}
{#if fight.ergebnis === 1}
<h1>{fight.blueTeam.name} gewinnt</h1>
{:else if fight.ergebnis === 2}
<h1>{fight.redTeam.name} gewinnt</h1>
{:else}
<h1>Unentschieden</h1>
{/if}
{:else}
<h1>{new Intl.DateTimeFormat("de-DE", {day: "2-digit", month: "2-digit", "year": "2-digit", hour: "2-digit", minute: "2-digit"}).format(fight.start)}</h1>
{/if}
</div>
{/each}
</div>
<div class="flex-grow"></div>
<div class="w-full mt-4 flex justify-between border rounded-xl p-2 self-end">
<h1 class="self-center">{event.event.name}</h1>
<h1 class="self-start">{new Intl.DateTimeFormat("de-DE", {hour: "2-digit", minute: "2-digit"}).format(clock)}</h1>
</div>
</div>
<style lang="scss">
::-webkit-scrollbar {
width: 0;
}
</style>

Datei anzeigen

@ -37,7 +37,7 @@
<p>{t("dashboard.stats.playtime", {playtime: new Intl.NumberFormat(astroI18n.locale, { <p>{t("dashboard.stats.playtime", {playtime: new Intl.NumberFormat(astroI18n.locale, {
minimumFractionDigits: 2, minimumFractionDigits: 2,
maximumFractionDigits: 2 maximumFractionDigits: 2
}).format(data.playtime)})}</p> }).format(data.playtime)})}h</p>
<p>{t("dashboard.stats.fights", {fights: data.fights})}</p> <p>{t("dashboard.stats.fights", {fights: data.fights})}</p>
{#if user.perms.includes("CHECK")} {#if user.perms.includes("CHECK")}
<p>{t("dashboard.stats.checked", {checked: data.acceptedSchematics})}</p> <p>{t("dashboard.stats.checked", {checked: data.acceptedSchematics})}</p>

Datei anzeigen

@ -91,5 +91,76 @@
"draw": "Unentschieden", "draw": "Unentschieden",
"points": "Punkte" "points": "Punkte"
} }
},
"blog": {
"title": "Ankündigungen - SteamWar"
},
"dashboard": {
"title": "Hallo, {# name #}!",
"rank": "Rang: {# rank #}",
"permissions": "Berechtigungen:",
"buttons": {
"logout": "Abmelden"
},
"stats": {
"playtime": "Spielzeit: {# playtime #}",
"fights": "Kämpfe: {# fights #}",
"checked": "Schematics Geprüft: {# checked #}"
},
"schematic": {
"upload": "Hochladen",
"dir": "Ordner",
"head": {
"type": "Typ",
"owner": "Besitzer",
"updated": "Aktualisiert",
"replaceColor": "Farbe ersetzen",
"allowReplay": "Wiederholung erlauben"
},
"cancel": "Abbrechen",
"title": "Schematic hochladen"
}
},
"login": {
"title": "Login",
"placeholder": {
"username": "Nutzername...",
"password": "***************"
},
"label": {
"username": "Nutzername",
"password": "Passwort"
},
"setPassword": "Wie setzte ich mein Passwort?",
"submit": "Login",
"error": "Falscher Nutzername oder falsches Passwort"
},
"ranked": {
"title": "{# mode #} - Rangliste"
},
"rules": {
"page": "SteamWar - Regelwerke",
"wg": {
"description": "Heute werden die Schlachtfelder der Erde von schwerem Geschütz bestimmt. Mit unserem traditionellen Regelwerk sind auch die WarGears arenenverwüstende Schwergewichte. Aufgrund der Kanonentechnik mit den meisten Projektilen erwarten dich bei WarGears harte und kurzweilige Kämpfe."
},
"mwg": {
"description": "Im heutigen Straßenkampf hat massives Gerät keinen Platz, weswegen kleinere Maschinen auch heute noch ihre Berechtigung haben. Mit den etwas kleineren Kanonen sind MiniWarGears genau das richtige für Einsteiger, Gelegenheitsspieler und Experimentierfreudige."
},
"ws": {
"description": "Lange Zeit waren Kriegsschiffe das Nonplusultra der Kriegsführung. In Sachen Raketen- und Slimetechnik gilt das auch heute noch für Warships. Durch die begrenzte Kanonenstärke bieten WarShips lange, intensive und abwechslungsreiche Kämpfe, womit es immer neue Technik in der Arena gibt. Durch das Entern verlagert sich ein WarShip-Kampf nach einiger Zeit in das Wasser und bietet damit spannende PvP-Action."
},
"as": {
"description": "Der Traum vom Fliegen beflügelt die Menschheit schon seit Jahrtausenden. Der Spielmodus AirShips bietet dir die nahezu unbegrenzten Möglichkeiten des Himmels. Egal, ob du mit 15 2 Projektil-Kanonen oder 2 15 Projektil-Kanonen antrittst, du hast stets eine realistische Chance auf den Sieg. Denn: Alles hat seinen Preis."
},
"qg": {
"description": "Nicht immer besteht die Zeit für einen langen Bau. Manchmal muss es schnell gehen. Für diese Fälle gibt es QuickGears. Ohne Qualitätsprüfung und mit nur einem Klick kannst du hier ein Gefährt erstellen. Die Qualität ist dabei nicht immer die beste, aber für einen schnellen Kampf reicht es allemal."
},
"rules": "Regelwerk »",
"announcements": "Ankündigungen »",
"ranking": "Rangliste »",
"title": "{# mode #} - Regelwerk"
},
"stats": {
"title": "Kampf Statistiken"
} }
} }

Datei anzeigen

@ -141,5 +141,97 @@
"warning": { "warning": {
"title": "This page is not available in your language.", "title": "This page is not available in your language.",
"text": "The page you are trying to access is not available in your language. You can still access the original page in German." "text": "The page you are trying to access is not available in your language. You can still access the original page in German."
},
"tag": {
"title": "Tag: {# tag #} - SteamWar"
},
"blog": {
"title": "Announcements - SteamWar"
},
"dashboard": {
"page": "SteamWar - Dashboard",
"title": "Hello, {# name #}!",
"rank": "Rank: {# rank #}",
"permissions": "Permssions:",
"buttons": {
"logout": "Logout",
"admin": "Admin Panel"
},
"stats": {
"playtime": "Playtime: {# playtime #}h",
"fights": "Fights: {# fights #}",
"checked": "Schematics Checked: {# checked #}"
},
"schematic": {
"upload": "Upload",
"cancel": "Cancel",
"title": "Upload Schematic",
"home": "Schematics",
"dir": "Directory",
"head": {
"type": "Type",
"name": "Name",
"owner": "Owner",
"updated": "Updated",
"replaceColor": "Replace Color",
"allowReplay": "Allow Replay"
},
"info": {
"path": "Path: {# path #}",
"replaceColor": "Replace Color: ",
"allowReplay": "Allow Replay: ",
"type": "Type: {# type #}",
"updated": "Updated: {# updated #}",
"item": "Item: {# item #}",
"members": "Members: {# members #}",
"btn": {
"download": "Download",
"close": "Close"
}
}
}
},
"login": {
"page": "SteamWar - Login",
"title": "Login",
"placeholder": {
"username": "Username...",
"password": "***************"
},
"label": {
"username": "Username",
"password": "Password"
},
"setPassword": "How to set a Password",
"submit": "Login",
"error": "Invalid username or password"
},
"ranked": {
"title": "{# mode #} - Ranking"
},
"rules": {
"page": "SteamWar - Rules",
"wg": {
"description": "Today, the battlefields of Earth are dominated by heavy artillery. With our traditional rules, WarGears are also arena-wrecking heavyweights. Due to the cannon technology with the most projectiles, you can expect hard and short-lived battles in WarGears."
},
"as": {
"description": "The dream of flying has inspired humanity for millennia. The AirShips game mode offers you the almost unlimited possibilities of the sky. Whether you compete with 15 2-projectile cannons or 2 15-projectile cannons, you always have a realistic chance of winning. Because: Everything has its price."
},
"ws": {
"description": "For a long time, warships were the ultimate weapon of war. This is still true for Warships today in terms of rocket and slime technology. Due to the limited cannon power, WarShips offer long, intense and varied battles, with new techniques always being introduced in the arena. After a while, a WarShip battle shifts to the water through boarding, providing exciting PvP action."
},
"mwg": {
"description": "In today's urban warfare, there is no place for heavy equipment, which is why smaller machines still have their place today. With their slightly smaller cannons, MiniWarGears are the perfect choice for beginners, casual players, and those who like to experiment."
},
"qg": {
"description": "Sometimes there is no time for a long construction. Sometimes it has to be quick. For these cases there are QuickGears. Without quality control and with just one click you can create a vehicle here. The quality is not always the best, but for a quick fight it is enough."
},
"rules": "Rules »",
"announcements": "Announcements »",
"ranking": "Ranking »",
"title": "{# mode #} - Rules"
},
"stats": {
"title": "Fight Statistics"
} }
} }

Datei anzeigen

@ -1,5 +0,0 @@
{
"blog": {
"title": "Ankündigungen - SteamWar"
}
}

Datei anzeigen

@ -1,5 +0,0 @@
{
"blog": {
"title": "Announcements - SteamWar"
}
}

Datei anzeigen

@ -1,5 +0,0 @@
{
"tag": {
"title": "Tag: {# tag #} - SteamWar"
}
}

Datei anzeigen

@ -1,28 +0,0 @@
{
"dashboard": {
"title": "Hallo, {# name #}!",
"rank": "Rang: {# rank #}",
"permissions": "Berechtigungen:",
"buttons": {
"logout": "Abmelden"
},
"stats": {
"playtime": "Spielzeit: {# playtime #}",
"fights": "Kämpfe: {# fights #}",
"checked": "Schematics Geprüft: {# checked #}"
},
"schematic": {
"upload": "Hochladen",
"dir": "Ordner",
"head": {
"type": "Typ",
"owner": "Besitzer",
"updated": "Aktualisiert",
"replaceColor": "Farbe ersetzen",
"allowReplay": "Wiederholung erlauben"
},
"cancel": "Abbrechen",
"title": "Schematic hochladen"
}
}
}

Datei anzeigen

@ -1,45 +0,0 @@
{
"dashboard": {
"page": "SteamWar - Dashboard",
"title": "Hello, {# name #}!",
"rank": "Rank: {# rank #}",
"permissions": "Permssions:",
"buttons": {
"logout": "Logout",
"admin": "Admin Panel"
},
"stats": {
"playtime": "Playtime: {# playtime #}h",
"fights": "Fights: {# fights #}",
"checked": "Schematics Checked: {# checked #}"
},
"schematic": {
"upload": "Upload",
"cancel": "Cancel",
"title": "Upload Schematic",
"home": "Schematics",
"dir": "Directory",
"head": {
"type": "Type",
"name": "Name",
"owner": "Owner",
"updated": "Updated",
"replaceColor": "Replace Color",
"allowReplay": "Allow Replay"
},
"info": {
"path": "Path: {# path #}",
"replaceColor": "Replace Color: ",
"allowReplay": "Allow Replay: ",
"type": "Type: {# type #}",
"updated": "Updated: {# updated #}",
"item": "Item: {# item #}",
"members": "Members: {# members #}",
"btn": {
"download": "Download",
"close": "Close"
}
}
}
}
}

Datei anzeigen

@ -1,16 +0,0 @@
{
"login": {
"title": "Login",
"placeholder": {
"username": "Nutzername...",
"password": "***************"
},
"label": {
"username": "Nutzername",
"password": "Passwort"
},
"setPassword": "Wie setzte ich mein Passwort?",
"submit": "Login",
"error": "Falscher Nutzername oder falsches Passwort"
}
}

Datei anzeigen

@ -1,17 +0,0 @@
{
"login": {
"page": "SteamWar - Login",
"title": "Login",
"placeholder": {
"username": "Username...",
"password": "***************"
},
"label": {
"username": "Username",
"password": "Password"
},
"setPassword": "How to set a Password",
"submit": "Login",
"error": "Invalid username or password"
}
}

Datei anzeigen

@ -1,5 +0,0 @@
{
"ranked": {
"title": "{# mode #} - Rangliste"
}
}

Datei anzeigen

@ -1,5 +0,0 @@
{
"ranked": {
"title": "{# mode #} - Ranking"
}
}

Datei anzeigen

@ -1,22 +0,0 @@
{
"page": "SteamWar - Regelwerke",
"wg": {
"description": "Heute werden die Schlachtfelder der Erde von schwerem Geschütz bestimmt. Mit unserem traditionellen Regelwerk sind auch die WarGears arenenverwüstende Schwergewichte. Aufgrund der Kanonentechnik mit den meisten Projektilen erwarten dich bei WarGears harte und kurzweilige Kämpfe."
},
"mwg": {
"description": "Im heutigen Straßenkampf hat massives Gerät keinen Platz, weswegen kleinere Maschinen auch heute noch ihre Berechtigung haben. Mit den etwas kleineren Kanonen sind MiniWarGears genau das richtige für Einsteiger, Gelegenheitsspieler und Experimentierfreudige."
},
"ws": {
"description": "Lange Zeit waren Kriegsschiffe das Nonplusultra der Kriegsführung. In Sachen Raketen- und Slimetechnik gilt das auch heute noch für Warships. Durch die begrenzte Kanonenstärke bieten WarShips lange, intensive und abwechslungsreiche Kämpfe, womit es immer neue Technik in der Arena gibt. Durch das Entern verlagert sich ein WarShip-Kampf nach einiger Zeit in das Wasser und bietet damit spannende PvP-Action."
},
"as": {
"description": "Der Traum vom Fliegen beflügelt die Menschheit schon seit Jahrtausenden. Der Spielmodus AirShips bietet dir die nahezu unbegrenzten Möglichkeiten des Himmels. Egal, ob du mit 15 2 Projektil-Kanonen oder 2 15 Projektil-Kanonen antrittst, du hast stets eine realistische Chance auf den Sieg. Denn: Alles hat seinen Preis."
},
"qg": {
"description": "Nicht immer besteht die Zeit für einen langen Bau. Manchmal muss es schnell gehen. Für diese Fälle gibt es QuickGears. Ohne Qualitätsprüfung und mit nur einem Klick kannst du hier ein Gefährt erstellen. Die Qualität ist dabei nicht immer die beste, aber für einen schnellen Kampf reicht es allemal."
},
"rules": "Regelwerk »",
"announcements": "Ankündigungen »",
"ranking": "Rangliste »",
"title": "{# mode #} - Regelwerk"
}

Datei anzeigen

@ -1,22 +0,0 @@
{
"page": "SteamWar - Rules",
"wg": {
"description": "Today, the battlefields of Earth are dominated by heavy artillery. With our traditional rules, WarGears are also arena-wrecking heavyweights. Due to the cannon technology with the most projectiles, you can expect hard and short-lived battles in WarGears."
},
"as": {
"description": "The dream of flying has inspired humanity for millennia. The AirShips game mode offers you the almost unlimited possibilities of the sky. Whether you compete with 15 2-projectile cannons or 2 15-projectile cannons, you always have a realistic chance of winning. Because: Everything has its price."
},
"ws": {
"description": "For a long time, warships were the ultimate weapon of war. This is still true for Warships today in terms of rocket and slime technology. Due to the limited cannon power, WarShips offer long, intense and varied battles, with new techniques always being introduced in the arena. After a while, a WarShip battle shifts to the water through boarding, providing exciting PvP action."
},
"mwg": {
"description": "In today's urban warfare, there is no place for heavy equipment, which is why smaller machines still have their place today. With their slightly smaller cannons, MiniWarGears are the perfect choice for beginners, casual players, and those who like to experiment."
},
"qg": {
"description": "Sometimes there is no time for a long construction. Sometimes it has to be quick. For these cases there are QuickGears. Without quality control and with just one click you can create a vehicle here. The quality is not always the best, but for a quick fight it is enough."
},
"rules": "Rules »",
"announcements": "Announcements »",
"ranking": "Ranking »",
"title": "{# mode #} - Rules"
}

Datei anzeigen

@ -1,5 +0,0 @@
{
"stats": {
"title": "Kampf Statistiken"
}
}

Datei anzeigen

@ -1,5 +0,0 @@
{
"stats": {
"title": "Fight Statistics"
}
}

Datei anzeigen

@ -2,8 +2,9 @@
import icon from "../images/logo.png"; import icon from "../images/logo.png";
import {getImage} from "astro:assets"; import {getImage} from "astro:assets";
import {astroI18n} from "astro-i18n"; import {astroI18n} from "astro-i18n";
const {title, description} = Astro.props.frontmatter || Astro.props;
import {SEO} from "astro-seo"; import {SEO} from "astro-seo";
import {ViewTransitions} from "astro:transitions";
const {title, description} = Astro.props.frontmatter || Astro.props;
import "../../public/fonts/roboto/roboto.css"; import "../../public/fonts/roboto/roboto.css";
@ -38,6 +39,8 @@ if (localStorage["theme-mode"] === "light" || (!("theme-mode" in localStorage) &
</script> </script>
<slot name="head"/> <slot name="head"/>
<ViewTransitions />
</head> </head>
<body class="dark:bg-zinc-800"> <body class="dark:bg-zinc-800">
<slot/> <slot/>

Datei anzeigen

@ -29,7 +29,7 @@ const {title, description} = Astro.props;
<div class="flex-1 flex justify-evenly items-center md:items-start mt-4 md:flex-row flex-col gap-y-4"> <div class="flex-1 flex justify-evenly items-center md:items-start mt-4 md:flex-row flex-col gap-y-4">
<div class="footer-card"> <div class="footer-card">
<h1>Serverstatus</h1> <h1>Serverstatus</h1>
<ServerStatus client:only="svelte"/> <ServerStatus transition:persist client:only="svelte"/>
</div> </div>
<div class="footer-card"> <div class="footer-card">
<h1>Links</h1> <h1>Links</h1>

Datei anzeigen

@ -11,7 +11,7 @@ const {title, description, image} = Astro.props;
<Image src={image || localBau} alt="Bau" width="1920" height="1080" densities={[1.5, 2, 3, 4]} <Image src={image || localBau} alt="Bau" width="1920" height="1080" densities={[1.5, 2, 3, 4]}
class="w-full h-screen dark:brightness-75 fixed -z-10 object-cover" draggable="false" /> class="w-full h-screen dark:brightness-75 fixed -z-10 object-cover" draggable="false" />
<div class="mx-auto bg-gray-100 p-8 rounded-b-md shadow-md pt-40 sm:pt-28 md:pt-14 relative <div class="mx-auto bg-gray-100 p-8 rounded-b-md shadow-md pt-40 sm:pt-28 md:pt-14 relative
dark:text-white dark:bg-neutral-900" style="width: min(100vw, 75em);"> dark:text-white dark:bg-neutral-900" style="width: min(100%, 75em);">
<slot/> <slot/>
</div> </div>
</NavbarLayout> </NavbarLayout>

Datei anzeigen

@ -7,10 +7,11 @@ import {t} from "astro-i18n";
<PageLayout title={t("dashboard.page")}> <PageLayout title={t("dashboard.page")}>
<script> <script>
import {l} from "../util/util"; import {l} from "../util/util";
import {navigate} from "astro:transitions/client";
if (window.location.href.endsWith("/dashboard") || window.location.href.endsWith("/dashboard/")) { if (window.location.href.endsWith("/dashboard") || window.location.href.endsWith("/dashboard/")) {
if ((localStorage.getItem("sw-session") ?? "") === "") { if ((localStorage.getItem("sw-session") ?? "") === "") {
window.location.href = l("/login"); navigate(l("/login"), {});
} }
} }
</script> </script>

Datei anzeigen

@ -9,10 +9,11 @@ import localBau from "@images/2023-10-08_20.43.43.png";
<NavbarLayout title={t("login.page")}> <NavbarLayout title={t("login.page")}>
<script> <script>
import {l} from "../util/util"; import {l} from "../util/util";
import {navigate} from "astro:transitions/client";
if (window.location.href.endsWith("/login") || window.location.href.endsWith("/login/")) { if (window.location.href.endsWith("/login") || window.location.href.endsWith("/login/")) {
if ((localStorage.getItem("sw-session") ?? "") !== "") { if ((localStorage.getItem("sw-session") ?? "") !== "") {
window.location.href = l("/dashboard"); navigate(l("/dashboard"));
} }
} }
</script> </script>

Datei anzeigen

@ -31,7 +31,7 @@ const {page, german} = Astro.props;
const {Content} = await page.render(); const {Content} = await page.render();
--- ---
<PageLayout title={t("title", {mode: t(`${page.data.translationKey}.title`)}, {route: "/rules"})}> <PageLayout title={t("rules.title", {mode: t(`${page.data.translationKey}.title`)})}>
<article> <article>
{german && ( {german && (
<LanguageWarning/> <LanguageWarning/>

Datei anzeigen

@ -20,22 +20,22 @@ const imageMap = {
const modes = await getCollection("modes", entry => entry.data.main); const modes = await getCollection("modes", entry => entry.data.main);
--- ---
<PageLayout title={t("page")}> <PageLayout title={t("rules.page")}>
{modes.map(value => ( {modes.map(value => (
<div class="dark:bg-neutral-800 rounded-md p-4 border border-neutral-400 shadow-md my-4 flex flex-col <div class="dark:bg-neutral-800 rounded-md p-4 border border-neutral-400 shadow-md my-4 flex flex-col
md:flex-row"> md:flex-row">
<Image height="300" width="300" src={imageMap[value.data.translationKey]} <Image height="300" width="300" src={imageMap[value.data.translationKey]}
alt={t(value.data.translationKey + ".title")} class="dark:invert"></Image> alt={t("rules." + value.data.translationKey + ".title")} class="dark:invert"></Image>
<div class="ml-4"> <div class="ml-4">
<h1 class="text-2xl font-bold">{t(value.data.translationKey + ".title")}</h1> <h1 class="text-2xl font-bold">{t(value.data.translationKey + ".title")}</h1>
<div>{t(value.data.translationKey + ".description")}</div> <div>{t("rules." + value.data.translationKey + ".description")}</div>
<div class="mt-2 flex flex-col"> <div class="mt-2 flex flex-col">
<a href={l(`/rules/${value.id}`)} class="text-yellow-300 hover:underline w-fit">{t("rules")}</a> <a href={l(`/rules/${value.id}`)} class="text-yellow-300 hover:underline w-fit">{t("rules.rules")}</a>
<a href={l(`/announcements/tags/${value.id}`)} <a href={l(`/announcements/tags/${value.id}`)}
class="text-yellow-300 hover:underline w-fit">{t("announcements")}</a> class="text-yellow-300 hover:underline w-fit">{t("rules.announcements")}</a>
{value.data.ranked {value.data.ranked
? <a href={l(`/ranked/${value.id}`)} ? <a href={l(`/ranked/${value.id}`)}
class="text-yellow-300 hover:underline w-fit">{t("ranking")}</a> class="text-yellow-300 hover:underline w-fit">{t("rules.ranking")}</a>
: null} : null}
</div> </div>
</div> </div>