Begin Display, Add View Transitions

This commit is contained in:
2024-02-28 17:28:21 +01:00
parent 361d7dae6a
commit 678746c89b
31 changed files with 349 additions and 212 deletions

View File

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

22
pnpm-lock.yaml generated
View File

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

View File

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

View File

@ -105,7 +105,7 @@
</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>
</a>
</div>

View File

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

View File

@ -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}

View File

@ -139,6 +139,10 @@
<ul>
{#if (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}
{@const nameRegexExec = nameRegex.exec(page.path)}
{@const match = nameRegexExec ? nameRegexExec[0] : ""}

View File

@ -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>

View File

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

View File

@ -91,5 +91,76 @@
"draw": "Unentschieden",
"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"
}
}

View File

@ -141,5 +141,97 @@
"warning": {
"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."
},
"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"
}
}

View File

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

View File

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

View File

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

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

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

View File

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

View File

@ -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"
}

View File

@ -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"
}

View File

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

View File

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

View File

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

View File

@ -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="footer-card">
<h1>Serverstatus</h1>
<ServerStatus client:only="svelte"/>
<ServerStatus transition:persist client:only="svelte"/>
</div>
<div class="footer-card">
<h1>Links</h1>

View File

@ -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]}
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
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/>
</div>
</NavbarLayout>

View File

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

View File

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

View File

@ -31,7 +31,7 @@ const {page, german} = Astro.props;
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>
{german && (
<LanguageWarning/>

View File

@ -20,22 +20,22 @@ const imageMap = {
const modes = await getCollection("modes", entry => entry.data.main);
---
<PageLayout title={t("page")}>
<PageLayout title={t("rules.page")}>
{modes.map(value => (
<div class="dark:bg-neutral-800 rounded-md p-4 border border-neutral-400 shadow-md my-4 flex flex-col
md:flex-row">
<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">
<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">
<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}`)}
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
? <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}
</div>
</div>