Dieser Commit ist enthalten in:
Ursprung
efd674eae1
Commit
9ee0fd5448
22
package.json
22
package.json
@ -18,32 +18,32 @@
|
||||
"ci": "pnpm run clean:dist && pnpm install && pnpm run i18n:sync && pnpm run build && pnpm run package"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/svelte": "^5.0.2",
|
||||
"@astrojs/tailwind": "^5.0.4",
|
||||
"@astrojs/svelte": "^5.0.3",
|
||||
"@astrojs/tailwind": "^5.1.0",
|
||||
"@astropub/icons": "^0.2.0",
|
||||
"@types/color": "^3.0.6",
|
||||
"@types/node": "^20.10.5",
|
||||
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
||||
"@typescript-eslint/parser": "^6.15.0",
|
||||
"@types/node": "^20.10.6",
|
||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||
"@typescript-eslint/parser": "^6.17.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"cssnano": "^6.0.2",
|
||||
"esbuild": "^0.19.10",
|
||||
"cssnano": "^6.0.3",
|
||||
"esbuild": "^0.19.11",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-astro": "^0.31.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"postcss-nesting": "^12.0.2",
|
||||
"sass": "^1.69.5",
|
||||
"sass": "^1.69.7",
|
||||
"svelte": "^4.2.8",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/sitemap": "^3.0.3",
|
||||
"@astrojs/sitemap": "^3.0.4",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@ddietr/codemirror-themes": "^1.4.2",
|
||||
"astro": "^4.0.7",
|
||||
"astro": "^4.1.0",
|
||||
"astro-i18n": "^2.2.0",
|
||||
"astro-robots-txt": "^1.0.0",
|
||||
"astro-seo": "^0.8.0",
|
||||
@ -57,7 +57,7 @@
|
||||
"flowbite-svelte": "^0.44.21",
|
||||
"flowbite-svelte-icons": "^0.4.5",
|
||||
"sharp": "^0.32.6",
|
||||
"svelte-awesome": "^3.3.0",
|
||||
"svelte-awesome": "^3.3.1",
|
||||
"svelte-codemirror-editor": "^1.2.0",
|
||||
"svelte-spa-router": "^3.3.0",
|
||||
"zod": "^3.22.4"
|
||||
|
1397
pnpm-lock.yaml
1397
pnpm-lock.yaml
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -22,11 +22,12 @@
|
||||
|
||||
export let selected: string | null = null
|
||||
export let items: {name: string, value: string}[] = []
|
||||
export let maxItems = 5;
|
||||
|
||||
export let searchValue = items.find(item => item.value === selected)?.name || ''
|
||||
let open = false
|
||||
|
||||
$: filteredItems = items.filter(item => item.name.toLowerCase().includes(searchValue.toLowerCase())).filter((value, index) => index < 5)
|
||||
$: filteredItems = items.filter(item => item.name.toLowerCase().includes(searchValue.toLowerCase())).filter((value, index) => index < maxItems)
|
||||
|
||||
function selectItem(item: {name: string, value: string}) {
|
||||
selected = item.value
|
||||
|
@ -18,27 +18,31 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import {ArrowLeftSolid} from "flowbite-svelte-icons";
|
||||
import {Button, Card, Navbar, NavBrand, Spinner} from "flowbite-svelte";
|
||||
import {mapToMap, nameRegex} from "../util.ts";
|
||||
import TypeAheadSearch from "../components/TypeAheadSearch.svelte";
|
||||
import {branches} from "@stores/stores.ts";
|
||||
import Editor from "./edit/Editor.svelte";
|
||||
import {pageRepo} from "@repo/page.ts";
|
||||
import {ArrowLeftSolid} from "flowbite-svelte-icons";
|
||||
import {Button, Card, Navbar, NavBrand, Spinner} from "flowbite-svelte";
|
||||
import {mapToMap, nameRegex} from "../util.ts";
|
||||
import TypeAheadSearch from "../components/TypeAheadSearch.svelte";
|
||||
import {branches} from "@stores/stores.ts";
|
||||
import Editor from "./edit/Editor.svelte";
|
||||
import {pageRepo} from "@repo/page.ts";
|
||||
|
||||
$: pagesFuture = $pageRepo.listPages(selectedBranch);
|
||||
$: pagesFuture = $pageRepo.listPages(selectedBranch);
|
||||
|
||||
let selected: number | null = null;
|
||||
let selected: number | null = null;
|
||||
|
||||
let selectedBranch: string = "master";
|
||||
let searchValue: string = "";
|
||||
let selectedBranch: string = "master";
|
||||
let searchValue: string = "";
|
||||
let dirty = false;
|
||||
|
||||
$: availableBranches = $branches.map((branch) => ({
|
||||
let selectedPath: string = "";
|
||||
let pathSearchValue: string = "";
|
||||
|
||||
$: availableBranches = $branches.map((branch) => ({
|
||||
name: branch,
|
||||
value: branch
|
||||
}))
|
||||
}))
|
||||
|
||||
async function createBranch() {
|
||||
async function createBranch() {
|
||||
const name = prompt("Branch name:")
|
||||
if (name) {
|
||||
selected = null
|
||||
@ -52,9 +56,20 @@ async function createBranch() {
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteBranch(con: boolean) {
|
||||
function changePage(id: number) {
|
||||
if (dirty) {
|
||||
if (confirm("You have unsaved changes. Are you sure you want to change the page?")) {
|
||||
selected = id
|
||||
dirty = false
|
||||
}
|
||||
} else {
|
||||
selected = id
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteBranch(con: boolean) {
|
||||
if (selectedBranch !== "master") {
|
||||
let conf = con || confirm("Are you sure you want to delete this branch?")
|
||||
if(conf) {
|
||||
@ -71,30 +86,24 @@ async function deleteBranch(con: boolean) {
|
||||
} else {
|
||||
alert("You can't delete the master branch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function createFile() {
|
||||
let name = prompt("File name:", "pages/en/[Name]")
|
||||
async function createFile() {
|
||||
let name = prompt("File name:", "[Name].md")
|
||||
if (name) {
|
||||
await $pageRepo.createFile(`${name}`, selectedBranch)
|
||||
await $pageRepo.createFile(`${selectedPath}${name}`, selectedBranch)
|
||||
reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reload() {
|
||||
function reload() {
|
||||
const w = selectedBranch
|
||||
selectedBranch = "###!"
|
||||
selectedBranch = w
|
||||
}
|
||||
|
||||
async function mergeBranch() {
|
||||
await $pageRepo.merge(selectedBranch, `Go live of ${selectedBranch}`)
|
||||
await deleteBranch(true)
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
<div class="flex flex-col h-screen overflow-scroll">
|
||||
<Navbar let:hidden let:toggle>
|
||||
<Navbar>
|
||||
<NavBrand href="#">
|
||||
<ArrowLeftSolid></ArrowLeftSolid>
|
||||
<span class="ml-4 self-center whitespace-nowrap text-xl font-semibold dark:text-white">
|
||||
@ -108,44 +117,42 @@ async function mergeBranch() {
|
||||
{#await pagesFuture}
|
||||
<Spinner />
|
||||
{:then pages}
|
||||
{@const pagesMap = mapToMap(pages)}
|
||||
<div class="border-b border-b-gray-600 pb-2 flex justify-between">
|
||||
<div>
|
||||
<TypeAheadSearch items={availableBranches} bind:selected={selectedBranch} bind:searchValue />
|
||||
<TypeAheadSearch items={Array.from(pagesMap.keys()).map(value => ({value, name: value}))} bind:selected={selectedPath} bind:searchValue={pathSearchValue} maxItems={Number.MAX_VALUE} />
|
||||
</div>
|
||||
<div>
|
||||
{#if selectedBranch !== "master"}
|
||||
<Button on:click={mergeBranch}>Merge Branch</Button>
|
||||
<Button on:click={createFile} color="alternative" disabled={!selectedPath}>Create File</Button>
|
||||
<Button on:click={() => deleteBranch(false)} color="none">Delete Branch</Button>
|
||||
{:else}
|
||||
<Button on:click={createBranch}>Create Branch</Button>
|
||||
{/if}
|
||||
<Button on:click={createFile} color="alternative">Create File</Button>
|
||||
{#if selectedBranch !== "master"}
|
||||
<Button on:click={() => deleteBranch(false)} color="none">Delete Branch</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{@const pagesMap = mapToMap(pages)}
|
||||
{#each pagesMap as [key, value]}
|
||||
<details>
|
||||
<summary class="p-4 transition-colors hover:bg-gray-700 cursor-pointer">{key}</summary>
|
||||
<ul>
|
||||
{#if (selectedPath)}
|
||||
{@const value = pagesMap.get(selectedPath) || []}
|
||||
{#each value as page}
|
||||
{@const nameRegexExec = nameRegex.exec(page.path)}
|
||||
{@const match = nameRegexExec ? nameRegexExec[0] : ""}
|
||||
{@const startIndex = page.path.indexOf(match)}
|
||||
{@const endIndex = startIndex + match.length}
|
||||
<li class="p-4 transition-colors hover:bg-gray-700 cursor-pointer" on:click|preventDefault={() => selected = page.id}>
|
||||
<li class="p-4 transition-colors hover:bg-gray-700 cursor-pointer" on:click|preventDefault={() => changePage(page.id)}>
|
||||
<span class:text-orange-600={selected === page.id}>{page.path.substring(0, startIndex)}</span><span class="text-white" class:!text-orange-500={selected === page.id}>{match}</span><span class:text-orange-600={selected === page.id}>{page.path.substring(endIndex, page.path.length)}</span>
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
</details>
|
||||
{/each}
|
||||
{:catch error}
|
||||
<p>{error.message}</p>
|
||||
{/await}
|
||||
</Card>
|
||||
<Card class="!max-w-full" style="grid-column: 2/4">
|
||||
{#if selected}
|
||||
<Editor pageId={selected} branch={selectedBranch} on:reload={reload} />
|
||||
<Editor pageId={selected} branch={selectedBranch} on:reload={reload} bind:dirty />
|
||||
{/if}
|
||||
</Card>
|
||||
</div>
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
export let pageId: number;
|
||||
export let branch: string;
|
||||
export let dirty: boolean = false;
|
||||
|
||||
let dispatcher = createEventDispatcher();
|
||||
|
||||
@ -47,6 +48,7 @@
|
||||
let message = window.prompt("Commit message:", "Update " + page!.name)
|
||||
if (message) {
|
||||
$pageRepo.updatePage(pageId, pageContent, page!.sha, message, branch)
|
||||
dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,11 +56,16 @@
|
||||
let message = window.prompt("Commit message:", "Delete " + page!.name)
|
||||
if (message) {
|
||||
await $pageRepo.deletePage(pageId, message, page!.sha, branch)
|
||||
dirty = false;
|
||||
dispatcher("reload")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:beforeunload={() => {
|
||||
if (dirty) {
|
||||
return "You have unsaved changes. Are you sure you want to leave?";
|
||||
}
|
||||
}} />
|
||||
{#await pageFuture}
|
||||
<Spinner />
|
||||
{:then p}
|
||||
@ -76,9 +83,9 @@
|
||||
</Toolbar>
|
||||
</div>
|
||||
{#if page?.name.endsWith("md")}
|
||||
<MDEMarkdownEditor bind:value={pageContent} />
|
||||
<MDEMarkdownEditor bind:value={pageContent} bind:dirty />
|
||||
{:else}
|
||||
<CodeMirror bind:value={pageContent} lang={json()} theme={materialDark} />
|
||||
<CodeMirror bind:value={pageContent} lang={json()} theme={materialDark} on:change={() => dirty = true} />
|
||||
{/if}
|
||||
</div>
|
||||
{:catch error}
|
||||
|
@ -23,6 +23,8 @@
|
||||
import "easymde/dist/easymde.min.css"
|
||||
|
||||
export let value: string;
|
||||
export let dirty: boolean = false;
|
||||
|
||||
let editor: HTMLTextAreaElement;
|
||||
let mde: EasyMDE;
|
||||
|
||||
@ -34,8 +36,10 @@
|
||||
})
|
||||
mde.codemirror.on("change", () => {
|
||||
value = mde.value();
|
||||
dirty = true;
|
||||
})
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
mde.toTextArea();
|
||||
mde.cleanup();
|
||||
|
@ -19,10 +19,11 @@
|
||||
|
||||
<script lang="ts">
|
||||
import {createEventDispatcher} from "svelte";
|
||||
import {Modal, Spinner} from "flowbite-svelte";
|
||||
import {Spinner} from "flowbite-svelte";
|
||||
import SchematicInfoModal from "./SchematicInfoModal.svelte";
|
||||
import type {Player} from "@type/data.ts";
|
||||
import {schemRepo} from "@repo/schem.ts";
|
||||
import SWModal from "@components/styled/SWModal.svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@ -37,14 +38,14 @@
|
||||
</script>
|
||||
|
||||
{#await schemInfo}
|
||||
<Modal title="Loading" open on:close={() => dispatch("reset")}>
|
||||
<SWModal title="Loading" open on:close={() => dispatch("reset")}>
|
||||
<Spinner />
|
||||
</Modal>
|
||||
</SWModal>
|
||||
{:then info}
|
||||
<SchematicInfoModal {user} {info} on:reset />
|
||||
{:catch e}
|
||||
<Modal title="Error" open on:close={() => dispatch("reset")}>
|
||||
<SWModal title="Error" open on:close={() => dispatch("reset")}>
|
||||
<p>{e.message}</p>
|
||||
<button class="btn !ml-auto" slot="footer" on:click={() => dispatch("reset")}>Close</button>
|
||||
</Modal>
|
||||
</SWModal>
|
||||
{/await}
|
@ -20,12 +20,12 @@
|
||||
<script lang="ts">
|
||||
import {astroI18n, t} from "astro-i18n";
|
||||
import {CheckSolid, XCircleOutline} from "flowbite-svelte-icons";
|
||||
import {Modal} from "flowbite-svelte";
|
||||
import type {SchematicInfo} from "@type/schem.ts";
|
||||
import {createEventDispatcher} from "svelte";
|
||||
import type {Player} from "@type/data.ts";
|
||||
import dayjs from "dayjs";
|
||||
import {schemRepo} from "@repo/schem.ts";
|
||||
import SWModal from "@components/styled/SWModal.svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal title={info.schem.name} autoclose open on:close={() => dispatch("reset")}>
|
||||
<SWModal title={info.schem.name} open on:close={() => dispatch("reset")}>
|
||||
<p>{t("dashboard.schematic.info.path", {path: info.path})}</p>
|
||||
<p class="flex !mt-0">
|
||||
{t("dashboard.schematic.info.replaceColor")}
|
||||
@ -76,4 +76,4 @@
|
||||
{/if}
|
||||
<button class="btn" class:!ml-auto={info.schem.owner !== user.id} on:click={() => dispatch("reset")}>{t("dashboard.schematic.info.btn.close")}</button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
</SWModal>
|
@ -150,7 +150,7 @@
|
||||
<div class="w-full flex justify-center mt-4">
|
||||
<ul class="inline-flex">
|
||||
<li>
|
||||
<button on:click={() => page = 0} class="btn btn-gray h-8 px-3 text-sm flex items-center !m-0 !rounded-l-none">
|
||||
<button on:click={() => page = 0} class="btn btn-gray h-8 px-3 text-sm flex items-center !m-0 !rounded-r-none">
|
||||
<span class="sr-only">Next</span>
|
||||
<ChevronDoubleLeftOutline class="w-3 h-3" />
|
||||
</button>
|
||||
|
@ -34,14 +34,13 @@
|
||||
{#await request}
|
||||
<p>{t("status.loading")}</p>
|
||||
{:then data}
|
||||
<p>Playtime: {new Intl.NumberFormat(astroI18n.locale, {
|
||||
<p>{t("dashboard.stats.playtime", {playtime: new Intl.NumberFormat(astroI18n.locale, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}).format(data.playtime)}h</p>
|
||||
<p>Fights: {data.fights}</p>
|
||||
}).format(data.playtime)})}</p>
|
||||
<p>{t("dashboard.stats.fights", {fights: data.fights})}</p>
|
||||
{#if user.perms.includes("CHECK")}
|
||||
<p>Schematics Checked: {data.acceptedSchematics}</p>
|
||||
<p>{t("dashboard.stats.checked", {checked: data.acceptedSchematics})}</p>
|
||||
{/if}
|
||||
{:catch error}
|
||||
<p>error: {error}</p>
|
||||
{/await}
|
@ -18,9 +18,10 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import {Modal} from "flowbite-svelte";
|
||||
import {createEventDispatcher} from "svelte";
|
||||
import {schemRepo} from "@repo/schem.ts";
|
||||
import SWModal from "@components/styled/SWModal.svelte";
|
||||
import {t} from "astro-i18n"
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@ -55,12 +56,12 @@
|
||||
let uploadFile: FileList | null = null;
|
||||
</script>
|
||||
|
||||
<Modal title="Upload Schematic" bind:open autoclose outsideclose>
|
||||
<SWModal title={t("dashboard.schematic.title")} bind:open>
|
||||
<form>
|
||||
<input type="file" bind:files={uploadFile} />
|
||||
</form>
|
||||
<svelte:fragment slot="footer">
|
||||
<button class="btn !ml-auto" on:click={upload}>Upload</button>
|
||||
<button class="btn btn-gray" on:click={() => open = false}>Close</button>
|
||||
<button class="btn !ml-auto" on:click={upload}>{t("dashboard.schematic.upload")}</button>
|
||||
<button class="btn btn-gray" on:click={() => open = false}>{t("dashboard.schematic.cancel")}</button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
</SWModal>
|
@ -24,6 +24,8 @@
|
||||
import Statistics from "./Statistics.svelte";
|
||||
import {authRepo} from "@repo/auth.ts";
|
||||
import {tokenStore} from "@repo/repo.ts";
|
||||
import SWModal from "@components/styled/SWModal.svelte";
|
||||
import SWButton from "@components/styled/SWButton.svelte";
|
||||
|
||||
export let user: Player;
|
||||
|
||||
@ -33,6 +35,7 @@
|
||||
window.location.href = l("/login")
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex mb-4 flex-col md:flex-row">
|
||||
<div>
|
||||
<div class="bg-zinc-50 border-gray-100 py-24 px-12 border-2 m-2 transition duration-300 ease-in-out rounded-xl shadow-lg hidden md:block
|
||||
|
88
src/components/styled/SWModal.svelte
Normale Datei
88
src/components/styled/SWModal.svelte
Normale Datei
@ -0,0 +1,88 @@
|
||||
<!--
|
||||
- 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 {createEventDispatcher, onMount} from "svelte";
|
||||
|
||||
export let title: string;
|
||||
export let open: boolean;
|
||||
let internalOpen = open;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
$: if (open && !internalOpen) {
|
||||
dialog.showModal();
|
||||
internalOpen = true;
|
||||
} else if (!open && internalOpen) {
|
||||
dialog.close();
|
||||
internalOpen = false;
|
||||
}
|
||||
|
||||
let dialog: HTMLDialogElement;
|
||||
|
||||
onMount(() => {
|
||||
if (open) {
|
||||
dialog.showModal();
|
||||
internalOpen = true;
|
||||
}
|
||||
});
|
||||
|
||||
function close() {
|
||||
open = false;
|
||||
internalOpen = false;
|
||||
dispatch("close");
|
||||
}
|
||||
</script>
|
||||
|
||||
<dialog bind:this={dialog} on:close={close} on:cancel={close} on:click={() => dialog.close()} aria-hidden="true">
|
||||
<div on:click|stopPropagation aria-hidden="true">
|
||||
<div class="spaced bordered">
|
||||
<h1>{title}</h1>
|
||||
</div>
|
||||
<div class="spaced main bordered">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="footer spaced" on:click={() => dialog.close()} aria-hidden="true">
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<style lang="scss">
|
||||
h1 {
|
||||
@apply text-4xl font-bold;
|
||||
}
|
||||
|
||||
dialog {
|
||||
@apply max-h-full max-w-md w-full rounded-lg shadow-lg
|
||||
dark:bg-neutral-800 dark:text-neutral-100;
|
||||
}
|
||||
|
||||
.spaced {
|
||||
@apply p-6;
|
||||
}
|
||||
|
||||
.bordered {
|
||||
@apply border-b border-neutral-200 dark:border-neutral-700;
|
||||
}
|
||||
|
||||
.footer {
|
||||
@apply flex mx-4 my-2;
|
||||
}
|
||||
</style>
|
38
src/content/announcements/de/airship-public-clash-2023-eventplan.md
Normale Datei
38
src/content/announcements/de/airship-public-clash-2023-eventplan.md
Normale Datei
@ -0,0 +1,38 @@
|
||||
---
|
||||
title: AirShip Public Clash 2023 Eventplan
|
||||
key: airship-public-clash-2023-eventplan
|
||||
description: Der Eventplan für den AirShip Public Clash 2023.
|
||||
created: 2023-12-16
|
||||
tags:
|
||||
- event
|
||||
- airship
|
||||
---
|
||||
|
||||
Eventleitung: TheBreadBeard
|
||||
Kampfleiter: TheBreadBeard, WarGear_Titan, Friderik (änderungen möglich)
|
||||
|
||||
| Team | Wins |
|
||||
|----------------------|------|
|
||||
| Defenders \[DFD] | 2 |
|
||||
| Gadgetron \[GAD] | / |
|
||||
| Halcyon \[Hlcy] | 6 |
|
||||
| 1gardedivision \[1.] | 3 |
|
||||
|
||||
|
||||
## 17.12.2023
|
||||
|
||||
| Startzeit | Begegnung |
|
||||
|-----------|-------------|
|
||||
| 16:00 | DFD vs GAD |
|
||||
| 16:00 | 1. vs Hlcy |
|
||||
| 16:20 | 1. vs GAD |
|
||||
| 16:20 | Hlcy vs DFD |
|
||||
| 16:40 | GAD vs Hlcy |
|
||||
| 16:40 | 1. vs DFD |
|
||||
| 17:00 | Pause |
|
||||
| 17:20 | Hlcy vs DFD |
|
||||
| 17:20 | 1. vs GAD |
|
||||
| 17:40 | Hlcy vs GAD |
|
||||
| 17:40 | DFD vs 1. |
|
||||
| 18:00 | Hlcy vs 1. |
|
||||
| 18:00 | DFD vs GAD |
|
@ -0,0 +1,50 @@
|
||||
---
|
||||
title: Bewerbungskriterien für den Supporter-Rang (Stand 2023)
|
||||
key: bk-supporter-2023
|
||||
description: Das sind die Bewerbungskriterien für den Supporter-Rang auf Steamwar.
|
||||
created: 2023-12-13
|
||||
tags:
|
||||
- team
|
||||
---
|
||||
|
||||
Wir haben uns dazu entschlossen die Bewerbungskriterien für den Supporterrang einmal zu überarbeiten.
|
||||
|
||||
Wichtig zu erwähnen ist noch, dass die unten genannten Kriterien nur als Richtwerte gelten. Das heißt solltet ihr einen oder mehrere dieser Kriterien nicht erfüllen, ist euere Chance angenommen zu werden zwar geringer aber nicht unbedingt gleich null.
|
||||
Das solltest du mitbringen:
|
||||
|
||||
- Alter von 16 Jahren oder die geistige Reife eines/r 16-jährigen
|
||||
- Gute sprachliche Fähigkeiten
|
||||
- Kompromissbereitschaft
|
||||
- Allgemeines Wissen in allen Hauptspielmodi von SteamWar sowie Kenntnis der Regeln dieser
|
||||
- Respektvolles und vorbildliches Verhalten (natürlich auf SteamWar und dem SteamWar-Discord selbst, aber auch auf anderen (Dc-)Server)
|
||||
- Generelles Interesse daran, SteamWar aktiv mitzugestalten und voran zu bringen
|
||||
|
||||
Nachweise, die du vorlegen können solltest
|
||||
|
||||
- 10 freigegebene Schematics (nach Möglichkeit aus verschiedenen Modi)
|
||||
- 1000 Spielstunden auf SteamWar
|
||||
- Teilnahme an 30 Fights
|
||||
- Teilnahme an 2 Events
|
||||
- Möglichst saubere Historie
|
||||
|
||||
Und als wichtigster Punkt solltest du das Vertrauen der Community genießen.
|
||||
|
||||
Eigenschaften:
|
||||
|
||||
Vertrauensvolle Beziehung zur Community: Vertrauen ist die Grundlage für den Rang des Supporters, sollte gegen dich als Bewerber ein hohes Misstrauen innerhalb der Community herrschen wegen z.B. aktuellen oder vergangenen Techklauvorwürfen bist du als Supporter eher ungeeignet. Wir raten dir dementsprechend derartige Vorwürfe oder Hörensagen aus der Welt zu schaffen und/oder klar zu stellen, bevor du dich bewirbst.
|
||||
|
||||
Allgemeines Wissen in allen Spielmodi: Als Supporter sollte dir jeder Spielmodus bekannt sein und du solltest in der Lage sein kurz zu erklären, worum es in dem Modus geht und was ihn ausmacht.
|
||||
|
||||
Kenntnis der Regeln: Du solltest mindestens alle Regelwerke der Hauptspielmodi kennen und simple Fragen zu diesen beantworten können. Am wichtigsten ist uns, dass du die Regelwerke verstanden hast und später im Sinne dieser prüfen kannst.
|
||||
|
||||
Respektvolles und vorbildliches Verhalten: Grundlegend sollten natürlich alle Spieler respektvoll miteinander umgehen. Wir wissen selber, dass das nicht immer perfekt funktioniert, du als Supporter solltest aber bemüht sein, auch in anspruchsvollen Situationen, dich respektvoll gegenüber anderen zu verhalten. Unter Respekt verstehen wir hierbei, dass du keinen Spieler schadest und diesen nicht in seiner Würde verletzt. Wir erwarten ebenfalls, dass du in Diskussionen versuchst, die Standpunkte aller Parteien zu verstehen und nicht einfach eine Partei als „dumm“ oder ähnliches abstempelst.
|
||||
|
||||
Auch auf anderen Server, insbesondere Discordservern von auf SteamWar vertretenden Teams, erwarten wir ein das oben genannte Verhalten.
|
||||
|
||||
Geistige Reife: Du solltest dir deiner Taten und ihrer Auswirkungen sowohl auf kurzer als auch langer Sicht bewusst sein und deine Entscheidungen stehts bedacht treffen.
|
||||
|
||||
Gute sprachliche Fähigkeiten: Unsere Community ist äußerst diskussionsfreundlich, als Supporter musst du dich im Gefecht der Wörter behaupten können. Dementsprechend wäre es vorteilhaft, einen großen Wortschatz und gute rhetorische Fähigkeiten zu besitzen. Es ist äußerst wichtig das du die Meinungen Anderer verstehst und damit umzugehen weißt. Ebenfalls wichtig ist, dass du die Meinung des Serverteams einbringen und vertreten kannst. (Da du selbst Teil des Serverteams bist, kannst du diese natürlich mitgestalten.)
|
||||
|
||||
Kompromissbereitschaft: Bei Konflikten gibt es meist keine Einigung, da die meisten ihre eigene Position nicht wechseln möchten und auch nicht auf die andere Partei zugehen wollen. Du als Supporter solltest zu dem genauem Gegenteil in der Lage sein. Du solltest Kompromisse finden können, die beide Parteien, zumindest grundlegend, zufriedenstellen können, ohne dabei eine Partei dabei stark zu bevorteilen.
|
||||
|
||||
Wenn du dich nun dazu entschlossen hast, dich als Supporter zu bewerben, freuen wir uns deine Bewerbung auf unserem Discordserver im Forum „bewerbungen“ lesen zu können. Stelle dich übrigens darauf ein, dass du Feedback und Fragen anderer Spieler erhältst.
|
@ -6,7 +6,6 @@ created: 2023-12-03
|
||||
tags:
|
||||
- event
|
||||
- microwargear
|
||||
image: ../../../images/bau.jpg
|
||||
---
|
||||
|
||||
Liebe Community!
|
||||
|
41
src/content/announcements/de/wargearseason2024.md
Normale Datei
41
src/content/announcements/de/wargearseason2024.md
Normale Datei
@ -0,0 +1,41 @@
|
||||
---
|
||||
title: WargearSeason2024
|
||||
key: wargearseason2024
|
||||
description: Die WargearSeason2024 steht an. Wir freuen uns auf Eure Teilnahme!
|
||||
created: 2023-12-17
|
||||
tags:
|
||||
- event
|
||||
- wargear
|
||||
image: ../../../images/WGS24B-1024x1024.png
|
||||
---
|
||||
|
||||
Ahoi Community,
|
||||
|
||||
Unser Jährliches Großevent die WGS steht wieder an. Zum 4. Mal in Folge rufen wir die Wargear Teams dazu auf Ihre Stärke unter beweis zu stellen.
|
||||
|
||||
Ab dem 9.03.2024 bis zum 27.04.2024 Wird jeden Samstag Abend wieder gekämpft!
|
||||
|
||||
Mit dem Befehl: /team Event WarGearSeason2024 oder über unsere Discord- Funktion können sich Teams für die WGS anmelden.
|
||||
|
||||
Anbei gebe ich Euch jetzt bereits einige Informationen zum Ablauf mit.
|
||||
|
||||
- Standart Wargear Regelwerk (1.20) ohne vor verbautes TNT
|
||||
- Anmeldeschluss: 17.02.2024
|
||||
- Max 6 Teams für die 1. Liga zugelassen
|
||||
- ggf Qualifikationskämpfe 02.03.2024
|
||||
- Start erster Kampftag: 9.03.2024
|
||||
- Finale: 27.04.2024
|
||||
- Jeden Samstag 18 Uhr wird gekämpft
|
||||
- Pro Team 2 Kämpfe je Kampftag
|
||||
- Osterpause: 30.03.2024
|
||||
- Tech KO durch Prozente Deaktiviert. Kampfleiter kann TechKO erklären
|
||||
- Aufgelöste, Inaktive Teams können durch ein anderes Team ersetzt werden
|
||||
- Teams dürfen nach einem Kampftag mit Niederlage ein neues WG einsenden
|
||||
- Min ein Vertreter Pro Team muss den WGS Discord beitreten
|
||||
- Eventleiter: Lixfel
|
||||
|
||||
Ich freue mich wieder auf eine aufregende und spannende Season und wünsche allen Team’s viel Spaß und Erfolg
|
||||
|
||||
Mit vielen Grüßen
|
||||
|
||||
Steamwar Serverteam
|
@ -18,17 +18,19 @@
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import {defineCollection, reference, z} from 'astro:content';
|
||||
import {defineCollection, reference, z} from "astro:content";
|
||||
|
||||
export const pages = defineCollection({
|
||||
type: "content",
|
||||
schema: z.object({
|
||||
export const pagesSchema = z.object({
|
||||
title: z.string().min(1).max(80),
|
||||
description: z.string().min(1).max(120),
|
||||
german: z.boolean().optional().default(false),
|
||||
image: z.string().optional()
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export const pages = defineCollection({
|
||||
type: "content",
|
||||
schema: pagesSchema
|
||||
});
|
||||
|
||||
export const help = defineCollection({
|
||||
type: "content",
|
||||
@ -36,9 +38,9 @@ export const help = defineCollection({
|
||||
title: z.string().min(1).max(80),
|
||||
description: z.string().min(1).max(120),
|
||||
tags: z.array(z.string()),
|
||||
related: z.array(reference('help')).optional()
|
||||
related: z.array(reference("help")).optional()
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export const modes = defineCollection({
|
||||
type: "data",
|
||||
@ -47,7 +49,7 @@ export const modes = defineCollection({
|
||||
main: z.boolean(),
|
||||
ranked: z.boolean().optional().default(false)
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export const downloads = defineCollection({
|
||||
type: "data",
|
||||
@ -58,14 +60,14 @@ export const downloads = defineCollection({
|
||||
.or(z.record(z.string(), z.string())),
|
||||
sourceUrl: z.string().url().optional(),
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export const rules = defineCollection({
|
||||
type: "content",
|
||||
schema: z.object({
|
||||
translationKey: z.string(),
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export const announcements = defineCollection({
|
||||
type: "content",
|
||||
@ -77,13 +79,13 @@ export const announcements = defineCollection({
|
||||
created: z.date(),
|
||||
key: z.string()
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
'pages': pages,
|
||||
'help': help,
|
||||
'modes': modes,
|
||||
'rules': rules,
|
||||
'downloads': downloads,
|
||||
'announcements': announcements
|
||||
}
|
||||
"pages": pages,
|
||||
"help": help,
|
||||
"modes": modes,
|
||||
"rules": rules,
|
||||
"downloads": downloads,
|
||||
"announcements": announcements
|
||||
};
|
||||
|
1
src/env.d.ts
vendored
1
src/env.d.ts
vendored
@ -24,7 +24,6 @@
|
||||
|
||||
/// <reference path="..\.astro-i18n\generated.d.ts" />
|
||||
|
||||
|
||||
// ###> astro-i18n/type-generation ###
|
||||
type PrimaryLocale = "en"
|
||||
type SecondaryLocale = "de"
|
||||
|
@ -6,6 +6,11 @@
|
||||
"buttons": {
|
||||
"logout": "Abmelden"
|
||||
},
|
||||
"stats": {
|
||||
"playtime": "Spielzeit: {# playtime #}",
|
||||
"fights": "Kämpfe: {# fights #}",
|
||||
"checked": "Schematics Geprüft: {# checked #}"
|
||||
},
|
||||
"schematic": {
|
||||
"upload": "Hochladen",
|
||||
"dir": "Ordner",
|
||||
@ -15,7 +20,9 @@
|
||||
"updated": "Aktualisiert",
|
||||
"replaceColor": "Farbe ersetzen",
|
||||
"allowReplay": "Wiederholung erlauben"
|
||||
}
|
||||
},
|
||||
"cancel": "Abbrechen",
|
||||
"title": "Schematic hochladen"
|
||||
}
|
||||
}
|
||||
}
|
@ -8,8 +8,15 @@
|
||||
"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": {
|
||||
|
@ -12,6 +12,9 @@
|
||||
"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 »"
|
||||
|
@ -12,6 +12,9 @@
|
||||
"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 »",
|
||||
|
BIN
src/images/WGS24B-1024x1024.png
Normale Datei
BIN
src/images/WGS24B-1024x1024.png
Normale Datei
Binäre Datei nicht angezeigt.
Nachher Breite: | Höhe: | Größe: 102 KiB |
@ -108,7 +108,7 @@ customElements.define("nav-bar", Navbar);
|
||||
<slot />
|
||||
</main>
|
||||
<footer class="bg-gray-900 w-full min-h-80 mt-4 pb-2 rounded-t-2xl flex flex-col dark:bg-neutral-900">
|
||||
<div class="flex-1 flex justify-evenly items-center md:items-start mt-4 md:flex-row flex-col">
|
||||
<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" />
|
||||
|
@ -70,7 +70,9 @@ const ogImage = await getImage({
|
||||
<article>
|
||||
<div class={"relative w-full " + (post.data.image ? "aspect-video" : "")}>
|
||||
{post.data.image && (
|
||||
<Image src={post.data.image} width="1920" height="1080" alt="" class="absolute top-0 left-0 w-full rounded-2xl linear-fade" />
|
||||
<div class="absolute top-0 left-0 w-full aspect-video flex justify-center">
|
||||
<Image src={post.data.image} height="1080" alt="" class="rounded-2xl linear-fade object-contain h-full" />
|
||||
</div>
|
||||
)}
|
||||
<div class={post.data.image ? "absolute bottom-8 left-2" : "mb-4"}>
|
||||
<h1 class="text-4xl mb-0">{post.data.title}</h1>
|
||||
|
@ -5,11 +5,13 @@ import {t} from "astro-i18n";
|
||||
---
|
||||
|
||||
<PageLayout title={t("dashboard.page")}>
|
||||
<script is:inline>
|
||||
<script>
|
||||
import {l} from "../util/util";
|
||||
if (window.location.href.endsWith("/dashboard") || window.location.href.endsWith("/dashboard/")) {
|
||||
if ((localStorage.getItem("sw-session") ?? "") === "") {
|
||||
window.location.href = l("/login");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<Dashboard client:only="svelte" />
|
||||
</PageLayout>
|
@ -7,15 +7,16 @@ import localBau from "@images/2023-10-08_20.43.43.png";
|
||||
---
|
||||
|
||||
<NavbarLayout title={t("login.page")}>
|
||||
<script is:inline>
|
||||
<script>
|
||||
import {l} from "../util/util";
|
||||
|
||||
if (window.location.href.endsWith("/login") || window.location.href.endsWith("/login/")) {
|
||||
if ((localStorage.getItem("sw-session") ?? "") !== "") {
|
||||
window.location.href = l("/dashboard");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<Image src={localBau} alt="Bau" width="1920" height="1080" class="w-screen h-screen dark:brightness-75 fixed -z-10 object-cover" draggable="false" />
|
||||
<div class="h-screen mx-auto p-8 rounded-b-md shadow-md pt-40 sm:pt-28 md:pt-14 flex flex-col justify-center items-center
|
||||
<div class="h-screen mx-auto p-8 rounded-b-md pt-40 sm:pt-28 md:pt-14 flex flex-col justify-center items-center
|
||||
dark:text-white " style="width: min(100vw, 75em);">
|
||||
<Login client:load />
|
||||
</div>
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren