Dieser Commit ist enthalten in:
Ursprung
4b27eb76fe
Commit
9fd8ddb9bd
2
.eslintignore
Normale Datei
2
.eslintignore
Normale Datei
@ -0,0 +1,2 @@
|
|||||||
|
src/pages/de
|
||||||
|
src/env.d.ts
|
@ -48,6 +48,15 @@
|
|||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"always"
|
"always"
|
||||||
]
|
],
|
||||||
|
"no-console": "error",
|
||||||
|
"no-debugger": "error",
|
||||||
|
"no-alert": "error",
|
||||||
|
"no-undef": "error",
|
||||||
|
"no-var": "error",
|
||||||
|
"no-const-assign": "error",
|
||||||
|
"comma-dangle": ["error", "always-multiline"],
|
||||||
|
"no-unneeded-ternary": "error",
|
||||||
|
"multiline-ternary": ["error", "always-multiline"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,17 +15,19 @@ const { post } = Astro.props as Props;
|
|||||||
|
|
||||||
<a href={l(`/announcements/${post.slug.split("/").slice(1).join("/")}`)}>
|
<a href={l(`/announcements/${post.slug.split("/").slice(1).join("/")}`)}>
|
||||||
<div class="p-4 flex flex-row">
|
<div class="p-4 flex flex-row">
|
||||||
{post.data.image != null ? (
|
{post.data.image != null
|
||||||
|
? (
|
||||||
<div class="flex-shrink-0 pr-2">
|
<div class="flex-shrink-0 pr-2">
|
||||||
<Image src={post.data.image} alt="Post Image" class="rounded-2xl shadow-2xl object-cover h-32 w-32 max-w-none transition-transform hover:scale-105" />
|
<Image src={post.data.image} alt="Post Image" class="rounded-2xl shadow-2xl object-cover h-32 w-32 max-w-none transition-transform hover:scale-105" />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
)
|
||||||
|
: null}
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-2xl font-bold">{post.data.title}</h2>
|
<h2 class="text-2xl font-bold">{post.data.title}</h2>
|
||||||
<P class="text-gray-500">{Intl.DateTimeFormat(astroI18n.locale, {
|
<P class="text-gray-500">{Intl.DateTimeFormat(astroI18n.locale, {
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
month: "long",
|
month: "long",
|
||||||
year: "numeric"
|
year: "numeric",
|
||||||
}).format(post.data.created)}</P>
|
}).format(post.data.created)}</P>
|
||||||
<P>{post.data.description}</P>
|
<P>{post.data.description}</P>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
|
@ -25,24 +25,39 @@
|
|||||||
import {tokenStore} from "@repo/repo";
|
import {tokenStore} from "@repo/repo";
|
||||||
|
|
||||||
const routes: RouteDefinition = {
|
const routes: RouteDefinition = {
|
||||||
'/': wrap({asyncComponent: () => import('./pages/Home.svelte'), conditions: detail => get(tokenStore) != ""}),
|
"/": wrap({asyncComponent: () => import("./pages/Home.svelte"), conditions: detail => get(tokenStore) != ""}),
|
||||||
'/perms': wrap({asyncComponent: () => import('./pages/Perms.svelte'), conditions: detail => get(tokenStore) != ""}),
|
"/perms": wrap({
|
||||||
'/login': wrap({asyncComponent: () => import('./pages/Login.svelte'), conditions: detail => get(tokenStore) == ""}),
|
asyncComponent: () => import("./pages/Perms.svelte"),
|
||||||
'/event/:id': wrap({asyncComponent: () => import('./pages/Event.svelte'), conditions: detail => get(tokenStore) != ""}),
|
conditions: detail => get(tokenStore) != ""
|
||||||
'/event/:id/generate': wrap({asyncComponent: () => import('./pages/Generate.svelte'), conditions: detail => get(tokenStore) != ""}),
|
}),
|
||||||
'/edit': wrap({asyncComponent: () => import('./pages/Edit.svelte'), conditions: detail => get(tokenStore) != ""}),
|
"/login": wrap({
|
||||||
'*': wrap({asyncComponent: () => import('./pages/NotFound.svelte')})
|
asyncComponent: () => import("./pages/Login.svelte"),
|
||||||
}
|
conditions: detail => get(tokenStore) == ""
|
||||||
|
}),
|
||||||
|
"/event/:id": wrap({
|
||||||
|
asyncComponent: () => import("./pages/Event.svelte"),
|
||||||
|
conditions: detail => get(tokenStore) != ""
|
||||||
|
}),
|
||||||
|
"/event/:id/generate": wrap({
|
||||||
|
asyncComponent: () => import("./pages/Generate.svelte"),
|
||||||
|
conditions: detail => get(tokenStore) != ""
|
||||||
|
}),
|
||||||
|
"/edit": wrap({
|
||||||
|
asyncComponent: () => import("./pages/Edit.svelte"),
|
||||||
|
conditions: detail => get(tokenStore) != ""
|
||||||
|
}),
|
||||||
|
"*": wrap({asyncComponent: () => import("./pages/NotFound.svelte")})
|
||||||
|
};
|
||||||
|
|
||||||
function conditionsFailed(event: ConditionsFailedEvent) {
|
function conditionsFailed(event: ConditionsFailedEvent) {
|
||||||
if(event.detail.location === "/login") {
|
if (event.detail.location === "/login") {
|
||||||
replace("/")
|
replace("/");
|
||||||
} else {
|
} else {
|
||||||
replace("/login")
|
replace("/login");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="dark:bg-gray-900 min-w-full min-h-screen text-gray-900 dark:text-gray-300">
|
<main class="dark:bg-gray-900 min-w-full min-h-screen text-gray-900 dark:text-gray-300">
|
||||||
<Router {routes} on:conditionsFailed={conditionsFailed} />
|
<Router {routes} on:conditionsFailed={conditionsFailed}/>
|
||||||
</main>
|
</main>
|
||||||
|
@ -37,48 +37,48 @@
|
|||||||
return {
|
return {
|
||||||
name: player.name,
|
name: player.name,
|
||||||
value: player.id.toString()
|
value: player.id.toString()
|
||||||
}
|
};
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
$: selectableTeams = teams.map(team => {
|
$: selectableTeams = teams.map(team => {
|
||||||
return {
|
return {
|
||||||
name: team.name,
|
name: team.name,
|
||||||
value: team.id.toString()
|
value: team.id.toString()
|
||||||
}
|
};
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
$: selectableGamemodes = $gamemodes.map(gamemode => {
|
$: selectableGamemodes = $gamemodes.map(gamemode => {
|
||||||
return {
|
return {
|
||||||
name: gamemode,
|
name: gamemode,
|
||||||
value: gamemode
|
value: gamemode
|
||||||
}
|
};
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||||
$: customGamemode = !selectableGamemodes.some((e) => e.name === gamemode) && gamemode !== '';
|
$: customGamemode = !selectableGamemodes.some((e) => e.name === gamemode) && gamemode !== "";
|
||||||
$: selectableCustomGamemode = [
|
$: selectableCustomGamemode = [
|
||||||
...selectableGamemodes, {
|
...selectableGamemodes, {
|
||||||
name: gamemode + ' (custom)',
|
name: gamemode + " (custom)",
|
||||||
value: gamemode
|
value: gamemode
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
|
||||||
$: mapsStore = maps(gamemode);
|
$: mapsStore = maps(gamemode);
|
||||||
$: selectableMaps = $mapsStore.map(map => {
|
$: selectableMaps = $mapsStore.map(map => {
|
||||||
return {
|
return {
|
||||||
name: map,
|
name: map,
|
||||||
value: map
|
value: map
|
||||||
}
|
};
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||||
$: customMap = !selectableMaps.some((e) => e.name === map) && map !== ''
|
$: customMap = !selectableMaps.some((e) => e.name === map) && map !== "";
|
||||||
$: selectableCustomMaps = [
|
$: selectableCustomMaps = [
|
||||||
...selectableMaps, {
|
...selectableMaps, {
|
||||||
name: map + ' (custom)',
|
name: map + " (custom)",
|
||||||
value: map
|
value: map
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
|
||||||
$: selectableGroups = [{
|
$: selectableGroups = [{
|
||||||
name: 'None',
|
name: "None",
|
||||||
value: ''
|
value: ""
|
||||||
}, {
|
}, {
|
||||||
value: groupSearch,
|
value: groupSearch,
|
||||||
name: `Create: '${groupSearch}'`
|
name: `Create: '${groupSearch}'`
|
||||||
@ -86,7 +86,7 @@
|
|||||||
return {
|
return {
|
||||||
name: group,
|
name: group,
|
||||||
value: group
|
value: group
|
||||||
}
|
};
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name))];
|
}).sort((a, b) => a.name.localeCompare(b.name))];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -106,11 +106,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="m-2">
|
<div class="m-2">
|
||||||
<Label for="fight-gamemode">Gamemode</Label>
|
<Label for="fight-gamemode">Gamemode</Label>
|
||||||
<Select items={customGamemode ? selectableCustomGamemode : selectableGamemodes} bind:value={gamemode} id="fight-gamemode"></Select>
|
<Select items={customGamemode ? selectableCustomGamemode : selectableGamemodes} bind:value={gamemode}
|
||||||
|
id="fight-gamemode"></Select>
|
||||||
</div>
|
</div>
|
||||||
<div class="m-2">
|
<div class="m-2">
|
||||||
<Label for="fight-maps">Map</Label>
|
<Label for="fight-maps">Map</Label>
|
||||||
<Select items={customMap ? selectableCustomMaps : selectableMaps} bind:value={map} id="fight-maps" disabled={customGamemode} class={customGamemode ? "cursor-not-allowed" : ""}></Select>
|
<Select items={customMap ? selectableCustomMaps : selectableMaps} bind:value={map} id="fight-maps"
|
||||||
|
disabled={customGamemode} class={customGamemode ? "cursor-not-allowed" : ""}></Select>
|
||||||
</div>
|
</div>
|
||||||
<div class="m-2">
|
<div class="m-2">
|
||||||
<Label for="fight-kampf">Kampfleiter</Label>
|
<Label for="fight-kampf">Kampfleiter</Label>
|
||||||
@ -118,5 +120,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="m-2">
|
<div class="m-2">
|
||||||
<Label for="fight-kampf">Group</Label>
|
<Label for="fight-kampf">Group</Label>
|
||||||
<TypeAheadSearch items={selectableGroups} bind:selected={group} bind:searchValue={groupSearch} all></TypeAheadSearch>
|
<TypeAheadSearch items={selectableGroups} bind:selected={group} bind:searchValue={groupSearch}
|
||||||
|
all></TypeAheadSearch>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,32 +18,35 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Button, Dropdown, Search} from 'flowbite-svelte'
|
import {Button, Dropdown, Search} from "flowbite-svelte";
|
||||||
|
|
||||||
export let selected: string | null = null
|
export let selected: string | null = null;
|
||||||
export let items: {name: string, value: string}[] = []
|
export let items: { name: string, value: string }[] = [];
|
||||||
export let maxItems = 5;
|
export let maxItems = 5;
|
||||||
export let leftText: boolean = false;
|
export let leftText: boolean = false;
|
||||||
|
|
||||||
export let searchValue = items.find(item => item.value === selected)?.name || ''
|
export let searchValue = items.find(item => item.value === selected)?.name || "";
|
||||||
let open = false
|
let open = false;
|
||||||
|
|
||||||
$: filteredItems = items.filter(item => item.name.toLowerCase().includes(searchValue.toLowerCase())).filter((value, index) => index < maxItems)
|
$: filteredItems = items.filter(item => item.name.toLowerCase().includes(searchValue.toLowerCase())).filter((value, index) => index < maxItems);
|
||||||
|
|
||||||
function selectItem(item: {name: string, value: string}) {
|
function selectItem(item: { name: string, value: string }) {
|
||||||
selected = item.value
|
selected = item.value;
|
||||||
searchValue = ""
|
searchValue = "";
|
||||||
open = false
|
open = false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Button color="light" on:click={() => open = true}>{selected === null ? 'Auswählen' : items.find(value => value.value === selected)?.name}</Button>
|
<Button color="light"
|
||||||
|
on:click={() => open = true}>{selected === null ? 'Auswählen' : items.find(value => value.value === selected)?.name}</Button>
|
||||||
<Dropdown bind:open class="w-60">
|
<Dropdown bind:open class="w-60">
|
||||||
<div class="overflow-y-auto p-3 text-sm w-60" slot="header">
|
<div class="overflow-y-auto p-3 text-sm w-60" slot="header">
|
||||||
<Search bind:value={searchValue} on:focus={() => open = true} on:keydown={() => open = true}/>
|
<Search bind:value={searchValue} on:focus={() => open = true} on:keydown={() => open = true}/>
|
||||||
</div>
|
</div>
|
||||||
{#each filteredItems as item (item)}
|
{#each filteredItems as item (item)}
|
||||||
<button on:click={() => selectItem(item)} class="rounded p-2 hover:bg-gray-100 dark:hover:bg-gray-600 w-full cursor-pointer border-b border-b-gray-600" class:text-left={leftText}>
|
<button on:click={() => selectItem(item)}
|
||||||
|
class="rounded p-2 hover:bg-gray-100 dark:hover:bg-gray-600 w-full cursor-pointer border-b border-b-gray-600"
|
||||||
|
class:text-left={leftText}>
|
||||||
{item.name}
|
{item.name}
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -40,68 +40,69 @@
|
|||||||
$: availableBranches = $branches.map((branch) => ({
|
$: availableBranches = $branches.map((branch) => ({
|
||||||
name: branch,
|
name: branch,
|
||||||
value: branch
|
value: branch
|
||||||
}))
|
}));
|
||||||
|
|
||||||
async function createBranch() {
|
async function createBranch() {
|
||||||
const name = prompt("Branch name:")
|
const name = prompt("Branch name:");
|
||||||
if (name) {
|
if (name) {
|
||||||
selected = null
|
selected = null;
|
||||||
await $pageRepo.createBranch(name)
|
await $pageRepo.createBranch(name);
|
||||||
let inter = setInterval(() => {
|
let inter = setInterval(() => {
|
||||||
branches.reload()
|
branches.reload();
|
||||||
if ($branches.includes(name)) {
|
if ($branches.includes(name)) {
|
||||||
selectedBranch = name
|
selectedBranch = name;
|
||||||
searchValue = ""
|
searchValue = "";
|
||||||
clearInterval(inter)
|
clearInterval(inter);
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function changePage(id: number) {
|
function changePage(id: number) {
|
||||||
if (dirty) {
|
if (dirty) {
|
||||||
if (confirm("You have unsaved changes. Are you sure you want to change the page?")) {
|
if (confirm("You have unsaved changes. Are you sure you want to change the page?")) {
|
||||||
selected = id
|
selected = id;
|
||||||
dirty = false
|
dirty = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
selected = id
|
selected = id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteBranch(con: boolean) {
|
async function deleteBranch(con: boolean) {
|
||||||
if (selectedBranch !== "master") {
|
if (selectedBranch !== "master") {
|
||||||
let conf = con || confirm("Are you sure you want to delete this branch?")
|
let conf = con || confirm("Are you sure you want to delete this branch?");
|
||||||
if(conf) {
|
if (conf) {
|
||||||
await $pageRepo.deleteBranch(selectedBranch)
|
await $pageRepo.deleteBranch(selectedBranch);
|
||||||
let inter = setInterval(() => {
|
let inter = setInterval(() => {
|
||||||
branches.reload()
|
branches.reload();
|
||||||
if (!$branches.includes(selectedBranch)) {
|
if (!$branches.includes(selectedBranch)) {
|
||||||
selectedBranch = "master"
|
selectedBranch = "master";
|
||||||
searchValue = ""
|
searchValue = "";
|
||||||
clearInterval(inter)
|
clearInterval(inter);
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert("You can't delete the master branch")
|
alert("You can't delete the master branch");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createFile() {
|
async function createFile() {
|
||||||
let name = prompt("File name:", "[Name].md")
|
let name = prompt("File name:", "[Name].md");
|
||||||
if (name) {
|
if (name) {
|
||||||
await $pageRepo.createFile(`${selectedPath}${name}`, selectedBranch)
|
await $pageRepo.createFile(`${selectedPath}${name}`, selectedBranch);
|
||||||
reload()
|
reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
const w = selectedBranch
|
const w = selectedBranch;
|
||||||
selectedBranch = "###!"
|
selectedBranch = "###!";
|
||||||
selectedBranch = w
|
selectedBranch = w;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col h-screen overflow-scroll">
|
<div class="flex flex-col h-screen overflow-scroll">
|
||||||
<Navbar>
|
<Navbar>
|
||||||
<NavBrand href="#">
|
<NavBrand href="#">
|
||||||
@ -115,17 +116,20 @@
|
|||||||
<div class="grid md:grid-cols-3 grid-cols-1 h-full gap-8">
|
<div class="grid md:grid-cols-3 grid-cols-1 h-full gap-8">
|
||||||
<Card class="h-full flex flex-col !max-w-full">
|
<Card class="h-full flex flex-col !max-w-full">
|
||||||
{#await pagesFuture}
|
{#await pagesFuture}
|
||||||
<Spinner />
|
<Spinner/>
|
||||||
{:then pages}
|
{:then pages}
|
||||||
{@const pagesMap = mapToMap(pages)}
|
{@const pagesMap = mapToMap(pages)}
|
||||||
<div class="border-b border-b-gray-600 pb-2 flex justify-between">
|
<div class="border-b border-b-gray-600 pb-2 flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
<TypeAheadSearch items={availableBranches} bind:selected={selectedBranch} bind:searchValue />
|
<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} leftText={true} />
|
<TypeAheadSearch items={Array.from(pagesMap.keys()).map(value => ({value, name: value}))}
|
||||||
|
bind:selected={selectedPath} bind:searchValue={pathSearchValue}
|
||||||
|
maxItems={Number.MAX_VALUE} leftText={true}/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{#if selectedBranch !== "master"}
|
{#if selectedBranch !== "master"}
|
||||||
<Button on:click={createFile} color="alternative" disabled={!selectedPath}>Create File</Button>
|
<Button on:click={createFile} color="alternative" disabled={!selectedPath}>Create File
|
||||||
|
</Button>
|
||||||
<Button on:click={() => deleteBranch(false)} color="none">Delete Branch</Button>
|
<Button on:click={() => deleteBranch(false)} color="none">Delete Branch</Button>
|
||||||
{:else}
|
{:else}
|
||||||
<Button on:click={createBranch}>Create Branch</Button>
|
<Button on:click={createBranch}>Create Branch</Button>
|
||||||
@ -140,8 +144,12 @@
|
|||||||
{@const match = nameRegexExec ? nameRegexExec[0] : ""}
|
{@const match = nameRegexExec ? nameRegexExec[0] : ""}
|
||||||
{@const startIndex = page.path.indexOf(match)}
|
{@const startIndex = page.path.indexOf(match)}
|
||||||
{@const endIndex = startIndex + match.length}
|
{@const endIndex = startIndex + match.length}
|
||||||
<li class="p-4 transition-colors hover:bg-gray-700 cursor-pointer" on:click|preventDefault={() => changePage(page.id)}>
|
<li class="p-4 transition-colors hover:bg-gray-700 cursor-pointer"
|
||||||
<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>
|
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>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
@ -152,7 +160,7 @@
|
|||||||
</Card>
|
</Card>
|
||||||
<Card class="!max-w-full" style="grid-column: 2/4">
|
<Card class="!max-w-full" style="grid-column: 2/4">
|
||||||
{#if selected}
|
{#if selected}
|
||||||
<Editor pageId={selected} branch={selectedBranch} on:reload={reload} bind:dirty />
|
<Editor pageId={selected} bind:branch={selectedBranch} on:reload={reload} bind:dirty/>
|
||||||
{/if}
|
{/if}
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
<Tabs style="pill" class="mx-4 flex shadow-lg border-b-2 border-gray-700 pb-2" contentClass="">
|
<Tabs style="pill" class="mx-4 flex shadow-lg border-b-2 border-gray-700 pb-2" contentClass="">
|
||||||
<TabItem open>
|
<TabItem open>
|
||||||
<span slot="title">Event</span>
|
<span slot="title">Event</span>
|
||||||
<EventEdit {data} />
|
<EventEdit {data}/>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem>
|
<TabItem>
|
||||||
<span slot="title">Teams</span>
|
<span slot="title">Teams</span>
|
||||||
|
@ -26,18 +26,18 @@
|
|||||||
import {eventRepo} from "@repo/event.ts";
|
import {eventRepo} from "@repo/event.ts";
|
||||||
import {tokenStore} from "@repo/repo.ts";
|
import {tokenStore} from "@repo/repo.ts";
|
||||||
|
|
||||||
let events = $eventRepo.listEvents()
|
let events = $eventRepo.listEvents();
|
||||||
let showAdd = false
|
let showAdd = false;
|
||||||
let millis = Date.now()
|
let millis = Date.now();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Navbar let:hidden let:toggle class="shadow-lg border-b">
|
<Navbar let:hidden let:toggle class="shadow-lg border-b">
|
||||||
<NavBrand href="#">
|
<NavBrand href="#">
|
||||||
<span class="self-center whitespace-nowrap text-xl font-semibold dark:text-white">
|
<span class="self-center whitespace-nowrap text-xl font-semibold dark:text-white">
|
||||||
Admin-Tool
|
Mod-Tool
|
||||||
</span>
|
</span>
|
||||||
</NavBrand>
|
</NavBrand>
|
||||||
<NavHamburger on:click={toggle} />
|
<NavHamburger on:click={toggle}/>
|
||||||
<NavUl {hidden}>
|
<NavUl {hidden}>
|
||||||
<NavLi href="#/edit">Edit Pages</NavLi>
|
<NavLi href="#/edit">Edit Pages</NavLi>
|
||||||
<NavLi href="#/perms">Permissions</NavLi>
|
<NavLi href="#/perms">Permissions</NavLi>
|
||||||
@ -58,13 +58,13 @@
|
|||||||
<h1 class="text-3xl mt-4 ml-4">Upcoming</h1>
|
<h1 class="text-3xl mt-4 ml-4">Upcoming</h1>
|
||||||
<div class="grid gap-4 p-4 border-b" style="grid-template-columns: repeat(auto-fill, minmax(300px, 1fr))">
|
<div class="grid gap-4 p-4 border-b" style="grid-template-columns: repeat(auto-fill, minmax(300px, 1fr))">
|
||||||
{#each data.filter((e) => e.start > millis) as event}
|
{#each data.filter((e) => e.start > millis) as event}
|
||||||
<EventCard {event} />
|
<EventCard {event}/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<h1 class="text-3xl mt-4 ml-4">Past</h1>
|
<h1 class="text-3xl mt-4 ml-4">Past</h1>
|
||||||
<div class="grid gap-4 p-4" style="grid-template-columns: repeat(auto-fill, minmax(300px, 1fr))">
|
<div class="grid gap-4 p-4" style="grid-template-columns: repeat(auto-fill, minmax(300px, 1fr))">
|
||||||
{#each data.filter((e) => e.start < millis).reverse() as event}
|
{#each data.filter((e) => e.start < millis).reverse() as event}
|
||||||
<EventCard {event} />
|
<EventCard {event}/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{:catch error}
|
{:catch error}
|
||||||
|
@ -31,9 +31,9 @@
|
|||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
loading = true;
|
loading = true;
|
||||||
let res = await fetchWithToken(value, "/data")
|
let res = await fetchWithToken(value, "/data");
|
||||||
loading = false;
|
loading = false;
|
||||||
if(res.ok) {
|
if (res.ok) {
|
||||||
$tokenStore = value;
|
$tokenStore = value;
|
||||||
await replace("/");
|
await replace("/");
|
||||||
} else {
|
} else {
|
||||||
@ -41,7 +41,7 @@
|
|||||||
value = "";
|
value = "";
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
error = false;
|
error = false;
|
||||||
}, 5000)
|
}, 5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -51,12 +51,13 @@
|
|||||||
<div class="grid gap-6 mb-6 md:grid-cols-1">
|
<div class="grid gap-6 mb-6 md:grid-cols-1">
|
||||||
<div>
|
<div>
|
||||||
<Label for="token-xyz" class="mb-2">Token</Label>
|
<Label for="token-xyz" class="mb-2">Token</Label>
|
||||||
<Input type={show?'text':'password'} id="token-xyz" placeholder="•••••••••" required size="lg" bind:value>
|
<Input type={show?'text':'password'} id="token-xyz" placeholder="•••••••••" required size="lg"
|
||||||
|
bind:value>
|
||||||
<button slot="left" on:click={() => (show = !show)} class="pointer-events-auto" type="button">
|
<button slot="left" on:click={() => (show = !show)} class="pointer-events-auto" type="button">
|
||||||
{#if show}
|
{#if show}
|
||||||
<EyeOutline />
|
<EyeOutline/>
|
||||||
{:else}
|
{:else}
|
||||||
<EyeSlashOutline />
|
<EyeSlashOutline/>
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
</Input>
|
</Input>
|
||||||
@ -64,7 +65,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<Button type="submit">
|
<Button type="submit">
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<Spinner size={4} class="mr-3" color="white"/> <span>Loading...</span>
|
<Spinner size={4} class="mr-3" color="white"/>
|
||||||
|
<span>Loading...</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span>Submit</span>
|
<span>Submit</span>
|
||||||
{/if}
|
{/if}
|
||||||
@ -74,7 +76,12 @@
|
|||||||
|
|
||||||
<Toast color="red" position="bottom-left" bind:open={error} transition={fly} params="{{x: -200}}">
|
<Toast color="red" position="bottom-left" bind:open={error} transition={fly} params="{{x: -200}}">
|
||||||
<svelte:fragment slot="icon">
|
<svelte:fragment slot="icon">
|
||||||
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||||
|
clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
<span class="sr-only">Error icon</span>
|
<span class="sr-only">Error icon</span>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
Invalid Token.
|
Invalid Token.
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
import {replace} from "svelte-spa-router";
|
import {replace} from "svelte-spa-router";
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
replace('/')
|
replace("/");
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
activePerms = value.perms;
|
activePerms = value.perms;
|
||||||
prefixEdit = value.prefix.name;
|
prefixEdit = value.prefix.name;
|
||||||
return value;
|
return value;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePerm(perm: string) {
|
function togglePerm(perm: string) {
|
||||||
@ -54,7 +54,7 @@
|
|||||||
} else {
|
} else {
|
||||||
activePerms = [...activePerms, perm];
|
activePerms = [...activePerms, perm];
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
@ -76,7 +76,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
playerPerms = loadPlayer(selectedPlayer);
|
playerPerms = loadPlayer(selectedPlayer);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let permsFuture = $permsRepo.listPerms();
|
let permsFuture = $permsRepo.listPerms();
|
||||||
@ -102,7 +102,9 @@
|
|||||||
{#if filteredPlayers.length < 100}
|
{#if filteredPlayers.length < 100}
|
||||||
<ul class="flex-1 overflow-scroll">
|
<ul class="flex-1 overflow-scroll">
|
||||||
{#each filteredPlayers as player (player.id)}
|
{#each filteredPlayers as player (player.id)}
|
||||||
<li class="p-4 transition-colors hover:bg-gray-700 cursor-pointer" class:text-orange-500={player.id === selectedPlayer} on:click|preventDefault={() => selectedPlayer = player.id}>
|
<li class="p-4 transition-colors hover:bg-gray-700 cursor-pointer"
|
||||||
|
class:text-orange-500={player.id === selectedPlayer}
|
||||||
|
on:click|preventDefault={() => selectedPlayer = player.id}>
|
||||||
{player.name}
|
{player.name}
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
@ -120,14 +122,18 @@
|
|||||||
{:then player}
|
{:then player}
|
||||||
<h1>Prefix</h1>
|
<h1>Prefix</h1>
|
||||||
{#each Object.entries(perms.prefixes) as [key, prefix]}
|
{#each Object.entries(perms.prefixes) as [key, prefix]}
|
||||||
<Radio name="prefix" bind:group={prefixEdit} value={prefix.name}>{capitalize(prefix.name.substring(7).toLowerCase())}</Radio>
|
<Radio name="prefix" bind:group={prefixEdit}
|
||||||
|
value={prefix.name}>{capitalize(prefix.name.substring(7).toLowerCase())}</Radio>
|
||||||
{/each}
|
{/each}
|
||||||
<h1>Permissions</h1>
|
<h1>Permissions</h1>
|
||||||
{#each perms.perms as perm}
|
{#each perms.perms as perm}
|
||||||
<Checkbox checked={activePerms.includes(perm)} on:click={togglePerm(perm)}>{capitalize(perm.toLowerCase())}</Checkbox>
|
<Checkbox checked={activePerms.includes(perm)}
|
||||||
|
on:click={togglePerm(perm)}>{capitalize(perm.toLowerCase())}</Checkbox>
|
||||||
{/each}
|
{/each}
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<Button disabled={prefixEdit === player.prefix.name && activePerms === player.perms} on:click={save}>Save</Button>
|
<Button disabled={prefixEdit === player.prefix.name && activePerms === player.perms}
|
||||||
|
on:click={save}>Save
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{:catch error}
|
{:catch error}
|
||||||
<p>{error.toString()}</p>
|
<p>{error.toString()}</p>
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
import CodeMirror from "svelte-codemirror-editor";
|
import CodeMirror from "svelte-codemirror-editor";
|
||||||
import {base64ToBytes} from "../../util.ts";
|
import {base64ToBytes} from "../../util.ts";
|
||||||
import type {Page} from "@type/page.ts";
|
import type {Page} from "@type/page.ts";
|
||||||
import {materialDark} from '@ddietr/codemirror-themes/material-dark.js'
|
import {materialDark} from "@ddietr/codemirror-themes/material-dark.js";
|
||||||
import {createEventDispatcher} from "svelte";
|
import {createEventDispatcher} from "svelte";
|
||||||
import MDEMarkdownEditor from "./MDEMarkdownEditor.svelte";
|
import MDEMarkdownEditor from "./MDEMarkdownEditor.svelte";
|
||||||
import {pageRepo} from "@repo/page.ts";
|
import {pageRepo} from "@repo/page.ts";
|
||||||
@ -40,24 +40,30 @@
|
|||||||
|
|
||||||
function getPage(value: Page): Page {
|
function getPage(value: Page): Page {
|
||||||
page = value;
|
page = value;
|
||||||
|
|
||||||
|
if (!dirty || confirm("You have unchanged Changes! Discard them? ")) {
|
||||||
|
navigator.clipboard.writeText(pageContent);
|
||||||
|
dirty = false;
|
||||||
pageContent = new TextDecoder().decode(base64ToBytes(value.content));
|
pageContent = new TextDecoder().decode(base64ToBytes(value.content));
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function savePage() {
|
function savePage() {
|
||||||
let message = window.prompt("Commit message:", "Update " + page!.name)
|
let message = window.prompt("Commit message:", "Update " + page!.name);
|
||||||
if (message) {
|
if (message) {
|
||||||
$pageRepo.updatePage(pageId, pageContent, page!.sha, message, branch)
|
$pageRepo.updatePage(pageId, pageContent, page!.sha, message, branch);
|
||||||
dirty = false;
|
dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deletePage() {
|
async function deletePage() {
|
||||||
let message = window.prompt("Commit message:", "Delete " + page!.name)
|
let message = window.prompt("Commit message:", "Delete " + page!.name);
|
||||||
if (message) {
|
if (message) {
|
||||||
await $pageRepo.deletePage(pageId, message, page!.sha, branch)
|
await $pageRepo.deletePage(pageId, message, page!.sha, branch);
|
||||||
dirty = false;
|
dirty = false;
|
||||||
dispatcher("reload")
|
dispatcher("reload");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -65,9 +71,9 @@
|
|||||||
if (dirty) {
|
if (dirty) {
|
||||||
return "You have unsaved changes. Are you sure you want to leave?";
|
return "You have unsaved changes. Are you sure you want to leave?";
|
||||||
}
|
}
|
||||||
}} />
|
}}/>
|
||||||
{#await pageFuture}
|
{#await pageFuture}
|
||||||
<Spinner />
|
<Spinner/>
|
||||||
{:then p}
|
{:then p}
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
@ -83,9 +89,9 @@
|
|||||||
</Toolbar>
|
</Toolbar>
|
||||||
</div>
|
</div>
|
||||||
{#if page?.name.endsWith("md")}
|
{#if page?.name.endsWith("md")}
|
||||||
<MDEMarkdownEditor bind:value={pageContent} bind:dirty />
|
<MDEMarkdownEditor bind:value={pageContent} bind:dirty/>
|
||||||
{:else}
|
{:else}
|
||||||
<CodeMirror bind:value={pageContent} lang={json()} theme={materialDark} on:change={() => dirty = true} />
|
<CodeMirror bind:value={pageContent} lang={json()} theme={materialDark} on:change={() => dirty = true}/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:catch error}
|
{:catch error}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onDestroy, onMount} from "svelte";
|
import {onDestroy, onMount} from "svelte";
|
||||||
import EasyMDE from "easymde";
|
import EasyMDE from "easymde";
|
||||||
import "easymde/dist/easymde.min.css"
|
import "easymde/dist/easymde.min.css";
|
||||||
|
|
||||||
export let value: string;
|
export let value: string;
|
||||||
export let dirty: boolean = false;
|
export let dirty: boolean = false;
|
||||||
@ -33,17 +33,17 @@
|
|||||||
element: editor,
|
element: editor,
|
||||||
initialValue: value,
|
initialValue: value,
|
||||||
spellChecker: false
|
spellChecker: false
|
||||||
})
|
});
|
||||||
mde.codemirror.on("change", () => {
|
mde.codemirror.on("change", () => {
|
||||||
value = mde.value();
|
value = mde.value();
|
||||||
dirty = true;
|
dirty = true;
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
mde.toTextArea();
|
mde.toTextArea();
|
||||||
mde.cleanup();
|
mde.cleanup();
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<textarea bind:this={editor} class="editor-preview">
|
<textarea bind:this={editor} class="editor-preview">
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
import {Button, Input, Label, Modal, Range, Select, Toast, Toggle} from "flowbite-svelte";
|
import {Button, Input, Label, Modal, Range, Select, Toast, Toggle} from "flowbite-svelte";
|
||||||
import {schemTypes} from "@stores/stores.ts";
|
import {schemTypes} from "@stores/stores.ts";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import utc from "dayjs/plugin/utc"
|
import utc from "dayjs/plugin/utc";
|
||||||
import {eventRepo, type UpdateEvent} from "@repo/event.ts";
|
import {eventRepo, type UpdateEvent} from "@repo/event.ts";
|
||||||
import ErrorModal from "../../components/ErrorModal.svelte";
|
import ErrorModal from "../../components/ErrorModal.svelte";
|
||||||
import {replace} from "svelte-spa-router";
|
import {replace} from "svelte-spa-router";
|
||||||
@ -55,7 +55,7 @@
|
|||||||
return {
|
return {
|
||||||
value: type.db,
|
value: type.db,
|
||||||
name: type.name
|
name: type.name
|
||||||
}
|
};
|
||||||
})];
|
})];
|
||||||
|
|
||||||
$: changed = name !== event.name ||
|
$: changed = name !== event.name ||
|
||||||
@ -70,7 +70,7 @@
|
|||||||
async function del() {
|
async function del() {
|
||||||
try {
|
try {
|
||||||
await $eventRepo.deleteEvent(event.id.toString());
|
await $eventRepo.deleteEvent(event.id.toString());
|
||||||
await replace("/")
|
await replace("/");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
errorOpen = true;
|
errorOpen = true;
|
||||||
@ -86,7 +86,7 @@
|
|||||||
maxTeamMembers: member,
|
maxTeamMembers: member,
|
||||||
name: name,
|
name: name,
|
||||||
publicSchemsOnly: publicOnly,
|
publicSchemsOnly: publicOnly,
|
||||||
schemType: schemType ?? 'null',
|
schemType: schemType ?? "null",
|
||||||
spectateSystem: spectateSystem,
|
spectateSystem: spectateSystem,
|
||||||
start: startDate
|
start: startDate
|
||||||
};
|
};
|
||||||
|
@ -40,14 +40,14 @@
|
|||||||
function dispatchSelect() {
|
function dispatchSelect() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!deleteOpen && !editOpen) {
|
if (!deleteOpen && !editOpen) {
|
||||||
dispatcher('select');
|
dispatcher("select");
|
||||||
}
|
}
|
||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteFight() {
|
async function deleteFight() {
|
||||||
await $fightRepo.deleteFight(fight.id);
|
await $fightRepo.deleteFight(fight.id);
|
||||||
dispatcher('update');
|
dispatcher("update");
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -93,14 +93,15 @@
|
|||||||
<EditOutline/>
|
<EditOutline/>
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
<ToolbarButton color="red" on:click={() => deleteOpen = true}>
|
<ToolbarButton color="red" on:click={() => deleteOpen = true}>
|
||||||
<TrashBinOutline />
|
<TrashBinOutline/>
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Modal title="Delete {fight.blueTeam.name} vs. {fight.redTeam.name}" bind:open={deleteOpen} autoclose outsideclose size="xs">
|
<Modal title="Delete {fight.blueTeam.name} vs. {fight.redTeam.name}" bind:open={deleteOpen} autoclose outsideclose
|
||||||
|
size="xs">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<p class="mb-5">
|
<p class="mb-5">
|
||||||
Are you sure you want to delete this fight?
|
Are you sure you want to delete this fight?
|
||||||
|
@ -40,8 +40,9 @@
|
|||||||
import TypeAheadSearch from "../../components/TypeAheadSearch.svelte";
|
import TypeAheadSearch from "../../components/TypeAheadSearch.svelte";
|
||||||
import {fightRepo, type UpdateFight} from "@repo/fight.ts";
|
import {fightRepo, type UpdateFight} from "@repo/fight.ts";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import duration from "dayjs/plugin/duration"
|
import duration from "dayjs/plugin/duration";
|
||||||
dayjs.extend(duration)
|
|
||||||
|
dayjs.extend(duration);
|
||||||
|
|
||||||
export let data: ExtendedEvent;
|
export let data: ExtendedEvent;
|
||||||
|
|
||||||
@ -54,13 +55,13 @@
|
|||||||
return {
|
return {
|
||||||
group: group,
|
group: group,
|
||||||
fights: fights.filter(fight => fight.group === group)
|
fights: fights.filter(fight => fight.group === group)
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function cycleSelect() {
|
function cycleSelect() {
|
||||||
if (selectedFights.size === fights.length) {
|
if (selectedFights.size === fights.length) {
|
||||||
selectedFights = new Set();
|
selectedFights = new Set();
|
||||||
} else if(selectedFights.size === 0){
|
} else if (selectedFights.size === 0) {
|
||||||
selectedFights = new Set(fights.filter(fight => fight.start > Date.now()));
|
selectedFights = new Set(fights.filter(fight => fight.start > Date.now()));
|
||||||
|
|
||||||
if (selectedFights.size === 0) {
|
if (selectedFights.size === 0) {
|
||||||
@ -72,7 +73,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cycleGroup(groupFights: EventFight[]) {
|
function cycleGroup(groupFights: EventFight[]) {
|
||||||
if(groupFights.every(gf => selectedFights.has(gf))) {
|
if (groupFights.every(gf => selectedFights.has(gf))) {
|
||||||
groupFights.forEach(fight => selectedFights.delete(fight));
|
groupFights.forEach(fight => selectedFights.delete(fight));
|
||||||
} else {
|
} else {
|
||||||
groupFights.forEach(fight => selectedFights.add(fight));
|
groupFights.forEach(fight => selectedFights.add(fight));
|
||||||
@ -81,6 +82,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let deleteOpen = false;
|
let deleteOpen = false;
|
||||||
|
|
||||||
async function deleteFights() {
|
async function deleteFights() {
|
||||||
for (const fight of selectedFights) {
|
for (const fight of selectedFights) {
|
||||||
await $fightRepo.deleteFight(fight.id);
|
await $fightRepo.deleteFight(fight.id);
|
||||||
@ -95,9 +97,10 @@
|
|||||||
return {
|
return {
|
||||||
name: player.name,
|
name: player.name,
|
||||||
value: player.id.toString()
|
value: player.id.toString()
|
||||||
}
|
};
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||||
let kampfleiter = "";
|
let kampfleiter = "";
|
||||||
|
|
||||||
async function updateKampfleiter() {
|
async function updateKampfleiter() {
|
||||||
for (const fight of selectedFights) {
|
for (const fight of selectedFights) {
|
||||||
let f: UpdateFight = {
|
let f: UpdateFight = {
|
||||||
@ -122,8 +125,8 @@
|
|||||||
let groupSearch = "";
|
let groupSearch = "";
|
||||||
|
|
||||||
$: selectableGroups = [{
|
$: selectableGroups = [{
|
||||||
name: 'None',
|
name: "None",
|
||||||
value: ''
|
value: ""
|
||||||
}, {
|
}, {
|
||||||
value: groupSearch,
|
value: groupSearch,
|
||||||
name: `Create: '${groupSearch}'`
|
name: `Create: '${groupSearch}'`
|
||||||
@ -131,8 +134,9 @@
|
|||||||
return {
|
return {
|
||||||
name: group,
|
name: group,
|
||||||
value: group
|
value: group
|
||||||
}
|
};
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name))];
|
}).sort((a, b) => a.name.localeCompare(b.name))];
|
||||||
|
|
||||||
async function updateGroup() {
|
async function updateGroup() {
|
||||||
for (const fight of selectedFights) {
|
for (const fight of selectedFights) {
|
||||||
let f: UpdateFight = {
|
let f: UpdateFight = {
|
||||||
@ -157,7 +161,7 @@
|
|||||||
let changeTimeOpen = false;
|
let changeTimeOpen = false;
|
||||||
let changedTime = fights.length != 0 ? dayjs(Math.min(...fights.map(fight => fight.start)))?.utc(true)?.toISOString()?.slice(0, -1) : undefined;
|
let changedTime = fights.length != 0 ? dayjs(Math.min(...fights.map(fight => fight.start)))?.utc(true)?.toISOString()?.slice(0, -1) : undefined;
|
||||||
|
|
||||||
$: deltaTime = dayjs.duration(dayjs(changedTime).utc(true).diff(minTime))
|
$: deltaTime = dayjs.duration(dayjs(changedTime).utc(true).diff(minTime));
|
||||||
|
|
||||||
async function updateStartTime() {
|
async function updateStartTime() {
|
||||||
for (const fight of selectedFights) {
|
for (const fight of selectedFights) {
|
||||||
@ -168,7 +172,7 @@
|
|||||||
map: null,
|
map: null,
|
||||||
redTeam: null,
|
redTeam: null,
|
||||||
spielmodus: null,
|
spielmodus: null,
|
||||||
start: dayjs(fight.start).add(deltaTime.asMilliseconds(), 'millisecond')
|
start: dayjs(fight.start).add(deltaTime.asMilliseconds(), "millisecond")
|
||||||
};
|
};
|
||||||
await $fightRepo.updateFight(fight.id, f);
|
await $fightRepo.updateFight(fight.id, f);
|
||||||
}
|
}
|
||||||
@ -194,7 +198,8 @@
|
|||||||
<CalendarWeekOutline/>
|
<CalendarWeekOutline/>
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
<Tooltip>Reschedule Fights</Tooltip>
|
<Tooltip>Reschedule Fights</Tooltip>
|
||||||
<ToolbarButton on:click={() => selectedFights.size > 0 ? kampfleiterOpen = true : kampfleiterOpen = false} disabled={changedTime === undefined}>
|
<ToolbarButton on:click={() => selectedFights.size > 0 ? kampfleiterOpen = true : kampfleiterOpen = false}
|
||||||
|
disabled={changedTime === undefined}>
|
||||||
<ProfileCardOutline/>
|
<ProfileCardOutline/>
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
<Tooltip>Change Kampfleiter</Tooltip>
|
<Tooltip>Change Kampfleiter</Tooltip>
|
||||||
@ -204,7 +209,8 @@
|
|||||||
<Tooltip>Change Group</Tooltip>
|
<Tooltip>Change Group</Tooltip>
|
||||||
</ToolbarGroup>
|
</ToolbarGroup>
|
||||||
<ToolbarGroup>
|
<ToolbarGroup>
|
||||||
<ToolbarButton color="red" on:click={() => selectedFights.size > 0 ? deleteOpen = true : deleteOpen = false}>
|
<ToolbarButton color="red"
|
||||||
|
on:click={() => selectedFights.size > 0 ? deleteOpen = true : deleteOpen = false}>
|
||||||
<TrashBinOutline/>
|
<TrashBinOutline/>
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
<Tooltip>Delete</Tooltip>
|
<Tooltip>Delete</Tooltip>
|
||||||
@ -212,7 +218,8 @@
|
|||||||
</Toolbar>
|
</Toolbar>
|
||||||
{#each groupedFights as group}
|
{#each groupedFights as group}
|
||||||
<div class="flex mt-4">
|
<div class="flex mt-4">
|
||||||
<Checkbox class="ml-2 text-center" checked={group.fights.every(gf => selectedFights.has(gf))} on:click={() => cycleGroup(group.fights)}/>
|
<Checkbox class="ml-2 text-center" checked={group.fights.every(gf => selectedFights.has(gf))}
|
||||||
|
on:click={() => cycleGroup(group.fights)}/>
|
||||||
<h1 class="ml-4 text-2xl">{group.group ?? "Ungrouped"}</h1>
|
<h1 class="ml-4 text-2xl">{group.group ?? "Ungrouped"}</h1>
|
||||||
</div>
|
</div>
|
||||||
{#each group.fights.sort((a, b) => a.start - b.start) as fight, i (fight.id)}
|
{#each group.fights.sort((a, b) => a.start - b.start) as fight, i (fight.id)}
|
||||||
@ -231,7 +238,8 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CreateFightModal {data} bind:open={createOpen} on:create={async () => data.fights = await $fightRepo.listFights(data.event.id)}></CreateFightModal>
|
<CreateFightModal {data} bind:open={createOpen}
|
||||||
|
on:create={async () => data.fights = await $fightRepo.listFights(data.event.id)}></CreateFightModal>
|
||||||
|
|
||||||
<Modal bind:open={deleteOpen} title="Delete {selectedFights.size} Fights" autoclose size="sm">
|
<Modal bind:open={deleteOpen} title="Delete {selectedFights.size} Fights" autoclose size="sm">
|
||||||
<p>Are you sure you want to delete {selectedFights.size} fights?</p>
|
<p>Are you sure you want to delete {selectedFights.size} fights?</p>
|
||||||
@ -255,7 +263,8 @@
|
|||||||
<Modal bind:open={groupChangeOpen} title="Change Group" size="sm">
|
<Modal bind:open={groupChangeOpen} title="Change Group" size="sm">
|
||||||
<div class="m-2">
|
<div class="m-2">
|
||||||
<Label for="fight-kampf">Group</Label>
|
<Label for="fight-kampf">Group</Label>
|
||||||
<TypeAheadSearch items={selectableGroups} bind:selected={group} bind:searchValue={groupSearch} all></TypeAheadSearch>
|
<TypeAheadSearch items={selectableGroups} bind:selected={group} bind:searchValue={groupSearch}
|
||||||
|
all></TypeAheadSearch>
|
||||||
</div>
|
</div>
|
||||||
<svelte:fragment slot="footer">
|
<svelte:fragment slot="footer">
|
||||||
<Button class="ml-auto" on:click={updateGroup}>Change</Button>
|
<Button class="ml-auto" on:click={updateGroup}>Change</Button>
|
||||||
@ -270,7 +279,8 @@
|
|||||||
<input type="datetime-local" {...props} bind:value={changedTime}/>
|
<input type="datetime-local" {...props} bind:value={changedTime}/>
|
||||||
</Input>
|
</Input>
|
||||||
</div>
|
</div>
|
||||||
<p>{deltaTime.asMilliseconds() < 0 ? '' : '+'}{("0" + deltaTime.hours()).slice(-2)}:{("0" + deltaTime.minutes()).slice(-2)}</p>
|
<p>{deltaTime.asMilliseconds() < 0 ? '' : '+'}{("0" + deltaTime.hours()).slice(-2)}
|
||||||
|
:{("0" + deltaTime.minutes()).slice(-2)}</p>
|
||||||
<svelte:fragment slot="footer">
|
<svelte:fragment slot="footer">
|
||||||
<Button class="ml-auto" on:click={updateStartTime}>Update</Button>
|
<Button class="ml-auto" on:click={updateStartTime}>Update</Button>
|
||||||
<Button on:click={() => changeTimeOpen = false} color="alternative">Cancel</Button>
|
<Button on:click={() => changeTimeOpen = false} color="alternative">Cancel</Button>
|
||||||
|
@ -29,7 +29,8 @@
|
|||||||
<Avatar size="lg">{team.kuerzel}</Avatar>
|
<Avatar size="lg">{team.kuerzel}</Avatar>
|
||||||
<div class="m-2">
|
<div class="m-2">
|
||||||
<h1 class="text-2xl">{team.name}</h1>
|
<h1 class="text-2xl">{team.name}</h1>
|
||||||
<h2 class="text-lg text-gray-400">Fights: {data.fights.filter(value => value.blueTeam.id === team.id || value.redTeam.id === team.id).length}</h2>
|
<h2 class="text-lg text-gray-400">
|
||||||
|
Fights: {data.fights.filter(value => value.blueTeam.id === team.id || value.redTeam.id === team.id).length}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -60,13 +60,13 @@
|
|||||||
kampfleiter: parseInt(kampfleiter!),
|
kampfleiter: parseInt(kampfleiter!),
|
||||||
group,
|
group,
|
||||||
});
|
});
|
||||||
reset()
|
reset();
|
||||||
|
|
||||||
dispatch("create")
|
dispatch("create");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
errorOpen = true;
|
errorOpen = true;
|
||||||
reset()
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
let blueTeam = fight.blueTeam.id.toString();
|
let blueTeam = fight.blueTeam.id.toString();
|
||||||
let start = dayjs(fight.start).utc(true).toISOString().slice(0, -1);
|
let start = dayjs(fight.start).utc(true).toISOString().slice(0, -1);
|
||||||
let kampfleiter = fight.kampfleiter?.id.toString();
|
let kampfleiter = fight.kampfleiter?.id.toString();
|
||||||
let gamemode = fight.spielmodus
|
let gamemode = fight.spielmodus;
|
||||||
let map = fight.map;
|
let map = fight.map;
|
||||||
let group = fight.group;
|
let group = fight.group;
|
||||||
let groupSearch = fight.group ?? "";
|
let groupSearch = fight.group ?? "";
|
||||||
@ -46,10 +46,17 @@
|
|||||||
let error: any = undefined;
|
let error: any = undefined;
|
||||||
|
|
||||||
let dispatch = createEventDispatcher();
|
let dispatch = createEventDispatcher();
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
const update: UpdateFight = {
|
const update: UpdateFight = {
|
||||||
blueTeam: parseInt(blueTeam), group: group === "" ? null : group, kampfleiter: parseInt(kampfleiter), map: map, redTeam: parseInt(redTeam), spielmodus: gamemode, start: dayjs(start)
|
blueTeam: parseInt(blueTeam),
|
||||||
}
|
group: group === "" ? null : group,
|
||||||
|
kampfleiter: parseInt(kampfleiter),
|
||||||
|
map: map,
|
||||||
|
redTeam: parseInt(redTeam),
|
||||||
|
spielmodus: gamemode,
|
||||||
|
start: dayjs(start)
|
||||||
|
};
|
||||||
|
|
||||||
$fightRepo.updateFight(fight.id, update)
|
$fightRepo.updateFight(fight.id, update)
|
||||||
.then(value => {
|
.then(value => {
|
||||||
@ -60,7 +67,7 @@
|
|||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
error = e.message;
|
error = e.message;
|
||||||
errorOpen = true;
|
errorOpen = true;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -32,11 +32,12 @@
|
|||||||
function handleDrop(ev: DragEvent) {
|
function handleDrop(ev: DragEvent) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
dragover = false;
|
dragover = false;
|
||||||
dispatch('drop', ev)
|
dispatch("drop", ev);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-56 bg-gray-800 p-4 rounded" class:border={dragover} class:m-px={!dragover} on:drop={handleDrop} on:dragover={handleDragOver} on:dragleave={() => dragover = false} role="none">
|
<div class="w-56 bg-gray-800 p-4 rounded" class:border={dragover} class:m-px={!dragover} on:drop={handleDrop}
|
||||||
|
on:dragover={handleDragOver} on:dragleave={() => dragover = false} role="none">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -42,14 +42,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function teamDragStart(ev: DragEvent, team: Team) {
|
function teamDragStart(ev: DragEvent, team: Team) {
|
||||||
ev.dataTransfer!.setData("team", team.id.toString())
|
ev.dataTransfer!.setData("team", team.id.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
let resetDragOver = false;
|
let resetDragOver = false;
|
||||||
|
|
||||||
function resetDragOverEvent(ev: DragEvent) {
|
function resetDragOverEvent(ev: DragEvent) {
|
||||||
resetDragOver = true;
|
resetDragOver = true;
|
||||||
ev.preventDefault()
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
function dropReset(ev: DragEvent) {
|
function dropReset(ev: DragEvent) {
|
||||||
@ -65,16 +65,16 @@
|
|||||||
groups = groups.map((group, i) => i === groupIndex ? [...group.filter(value => value != teamId), teamId] : group.filter(value => value != teamId)).filter(group => group.length > 0);
|
groups = groups.map((group, i) => i === groupIndex ? [...group.filter(value => value != teamId), teamId] : group.filter(value => value != teamId)).filter(group => group.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let startTime = dayjs(data.event.start).utc(true).toISOString().slice(0, -1)
|
let startTime = dayjs(data.event.start).utc(true).toISOString().slice(0, -1);
|
||||||
$: startMoment = dayjs(startTime);
|
$: startMoment = dayjs(startTime);
|
||||||
let gamemode = ''
|
let gamemode = "";
|
||||||
let map = ''
|
let map = "";
|
||||||
|
|
||||||
$: selectableGamemodes = $gamemodes.map(gamemode => {
|
$: selectableGamemodes = $gamemodes.map(gamemode => {
|
||||||
return {
|
return {
|
||||||
name: gamemode,
|
name: gamemode,
|
||||||
value: gamemode
|
value: gamemode
|
||||||
}
|
};
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
$: mapsStore = maps(gamemode);
|
$: mapsStore = maps(gamemode);
|
||||||
@ -82,7 +82,7 @@
|
|||||||
return {
|
return {
|
||||||
name: map,
|
name: map,
|
||||||
value: map
|
value: map
|
||||||
}
|
};
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
let roundTime = 30;
|
let roundTime = 30;
|
||||||
@ -95,10 +95,10 @@
|
|||||||
let teams = data.teams.map(team => team.id).sort(() => Math.random() - 0.5);
|
let teams = data.teams.map(team => team.id).sort(() => Math.random() - 0.5);
|
||||||
groups = [];
|
groups = [];
|
||||||
for (let i = 0; i < groupCount; i++) {
|
for (let i = 0; i < groupCount; i++) {
|
||||||
groups.push([])
|
groups.push([]);
|
||||||
}
|
}
|
||||||
while (teams.length > 0) {
|
while (teams.length > 0) {
|
||||||
groups[teams.length % groupCount].push(teams.pop() as number)
|
groups[teams.length % groupCount].push(teams.pop() as number);
|
||||||
}
|
}
|
||||||
showAutoGrouping = false;
|
showAutoGrouping = false;
|
||||||
groups = groups.filter(group => group.length > 0);
|
groups = groups.filter(group => group.length > 0);
|
||||||
@ -111,8 +111,8 @@
|
|||||||
let groupFight = [];
|
let groupFight = [];
|
||||||
for (let i = 0; i < round; i++) {
|
for (let i = 0; i < round; i++) {
|
||||||
let availableTeams = [...group];
|
let availableTeams = [...group];
|
||||||
if(group.length % 2 === 1) {
|
if (group.length % 2 === 1) {
|
||||||
availableTeams = availableTeams.filter((team, index) => index !== i)
|
availableTeams = availableTeams.filter((team, index) => index !== i);
|
||||||
}
|
}
|
||||||
let roundFights = [];
|
let roundFights = [];
|
||||||
while (availableTeams.length > 0) {
|
while (availableTeams.length > 0) {
|
||||||
@ -121,25 +121,25 @@
|
|||||||
availableTeams = availableTeams.filter(team => team !== team2);
|
availableTeams = availableTeams.filter(team => team !== team2);
|
||||||
let fight = [team1, team2];
|
let fight = [team1, team2];
|
||||||
fight.sort(() => Math.random() - 0.5);
|
fight.sort(() => Math.random() - 0.5);
|
||||||
roundFights.push(fight)
|
roundFights.push(fight);
|
||||||
}
|
}
|
||||||
groupFight.push(roundFights)
|
groupFight.push(roundFights);
|
||||||
}
|
}
|
||||||
groupFights.push(groupFight)
|
groupFights.push(groupFight);
|
||||||
})
|
});
|
||||||
return groupFights;
|
return groupFights;
|
||||||
}
|
}
|
||||||
|
|
||||||
$: groupsFights = generateGroups(groups)
|
$: groupsFights = generateGroups(groups);
|
||||||
|
|
||||||
$: generateDisabled = groupsFights.length > 0 && groupsFights.every(value => value.every(value1 => value1.length > 0)) && gamemode !== '' && map !== ''
|
$: generateDisabled = groupsFights.length > 0 && groupsFights.every(value => value.every(value1 => value1.length > 0)) && gamemode !== "" && map !== "";
|
||||||
|
|
||||||
async function generateFights() {
|
async function generateFights() {
|
||||||
groupsFights.forEach((group, i) => {
|
groupsFights.forEach((group, i) => {
|
||||||
group.forEach((round, j) => {
|
group.forEach((round, j) => {
|
||||||
round.forEach(async (fight, k) => {
|
round.forEach(async (fight, k) => {
|
||||||
const blueTeam = teams.get(fight[0])!
|
const blueTeam = teams.get(fight[0])!;
|
||||||
const redTeam = teams.get(fight[1])!
|
const redTeam = teams.get(fight[1])!;
|
||||||
|
|
||||||
await $fightRepo.createFight(data.event.id, {
|
await $fightRepo.createFight(data.event.id, {
|
||||||
blueTeam: blueTeam.id,
|
blueTeam: blueTeam.id,
|
||||||
@ -149,17 +149,19 @@
|
|||||||
map: map,
|
map: map,
|
||||||
spielmodus: gamemode,
|
spielmodus: gamemode,
|
||||||
start: startMoment.clone().add(roundTime * j, "minutes").add(startDelay * (k + (i * round.length)), "seconds")
|
start: startMoment.clone().add(roundTime * j, "minutes").add(startDelay * (k + (i * round.length)), "seconds")
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
await replace("#/event/" + data.event.id)
|
await replace("#/event/" + data.event.id);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div id="reseter" class:border-white={resetDragOver} class="flex m-2 bg-gray-800 w-fit p-2 border border-gray-700 rounded ml-4 h-20 pt-6 relative" on:dragover={resetDragOverEvent} on:dragleave={() => resetDragOver = false} on:drop={dropReset} role="group">
|
<div id="reseter" class:border-white={resetDragOver}
|
||||||
|
class="flex m-2 bg-gray-800 w-fit p-2 border border-gray-700 rounded ml-4 h-20 pt-6 relative"
|
||||||
|
on:dragover={resetDragOverEvent} on:dragleave={() => resetDragOver = false} on:drop={dropReset} role="group">
|
||||||
{#each teamsNotInGroup as team (team.id)}
|
{#each teamsNotInGroup as team (team.id)}
|
||||||
<TeamChip {team} on:dragstart={ev => teamDragStart(ev, team)}/>
|
<TeamChip {team} on:dragstart={ev => teamDragStart(ev, team)}/>
|
||||||
{/each}
|
{/each}
|
||||||
@ -247,6 +249,7 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
color: gray;
|
color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
#reseter {
|
#reseter {
|
||||||
min-width: 14rem;
|
min-width: 14rem;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="rounded w-fit p-2 border-gray-600 border cursor-grab select-none m-1 flex place-items-center"
|
<div class="rounded w-fit p-2 border-gray-600 border cursor-grab select-none m-1 flex place-items-center"
|
||||||
style:background-color={hover ? lighten(colorFromTeam(team)) : colorFromTeam(team)} class:text-black={brightness(colorFromTeam(team))} draggable="true"
|
style:background-color={hover ? lighten(colorFromTeam(team)) : colorFromTeam(team)}
|
||||||
|
class:text-black={brightness(colorFromTeam(team))} draggable="true"
|
||||||
on:dragstart
|
on:dragstart
|
||||||
on:mouseenter={() => hover = true}
|
on:mouseenter={() => hover = true}
|
||||||
on:mouseleave={() => hover = false}
|
on:mouseleave={() => hover = false}
|
||||||
|
@ -32,11 +32,11 @@
|
|||||||
|
|
||||||
let eventName = "";
|
let eventName = "";
|
||||||
let start = "";
|
let start = "";
|
||||||
$: startDate = dayjs(start)
|
$: startDate = dayjs(start);
|
||||||
let end = "";
|
let end = "";
|
||||||
$: endDate = dayjs(end)
|
$: endDate = dayjs(end);
|
||||||
|
|
||||||
$: canSubmit = eventName.length > 0 && startDate.isValid() && endDate.isValid() && startDate.isBefore(endDate)
|
$: canSubmit = eventName.length > 0 && startDate.isValid() && endDate.isValid() && startDate.isBefore(endDate);
|
||||||
|
|
||||||
async function createEvent() {
|
async function createEvent() {
|
||||||
try {
|
try {
|
||||||
@ -44,7 +44,7 @@
|
|||||||
name: eventName,
|
name: eventName,
|
||||||
start: startDate,
|
start: startDate,
|
||||||
end: endDate
|
end: endDate
|
||||||
})
|
});
|
||||||
dispatch("create");
|
dispatch("create");
|
||||||
open = false;
|
open = false;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Card} from "flowbite-svelte";
|
import {Card} from "flowbite-svelte";
|
||||||
import {link} from 'svelte-spa-router'
|
import {link} from "svelte-spa-router";
|
||||||
import type {ShortEvent} from "@type/event.ts";
|
import type {ShortEvent} from "@type/event.ts";
|
||||||
|
|
||||||
export let event: ShortEvent;
|
export let event: ShortEvent;
|
||||||
|
@ -23,7 +23,7 @@ import type {ListPage, PageList} from "@type/page.ts";
|
|||||||
|
|
||||||
export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
|
export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
|
||||||
export const nameRegex = new RegExp("(?!.*\/).+(?=\\.(md|json))");
|
export const nameRegex = new RegExp("(?!.*/).+(?=\\.(md|json))");
|
||||||
|
|
||||||
export function mapToMap(pages: PageList): Map<string, ListPage[]> {
|
export function mapToMap(pages: PageList): Map<string, ListPage[]> {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
@ -82,9 +82,9 @@ export function brightness(color: string) {
|
|||||||
return Color(color).isLight();
|
return Color(color).isLight();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function base64ToBytes(base64: string) {
|
export function base64ToBytes(base64: string): Uint8Array {
|
||||||
const binString = atob(base64);
|
const binString = atob(base64);
|
||||||
// @ts-ignore
|
// @ts-expect-error Some Function Missing
|
||||||
return Uint8Array.from(binString, (m) => m.codePointAt(0));
|
return Uint8Array.from(binString, (m) => m.codePointAt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,15 +28,15 @@ export class AuthRepo {
|
|||||||
return await fetchWithToken(this.token, "/auth/login", {
|
return await fetchWithToken(this.token, "/auth/login", {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username,
|
username,
|
||||||
password
|
password,
|
||||||
}),
|
}),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}).then(value => value.json()).then(value => value.token)
|
}).then(value => value.json()).then(value => value.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async logout(): Promise<void> {
|
public async logout(): Promise<void> {
|
||||||
await fetchWithToken(this.token, "/auth/tokens/logout", {
|
await fetchWithToken(this.token, "/auth/tokens/logout", {
|
||||||
method: "POST"
|
method: "POST",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ import {fetchWithToken, tokenStore} from "./repo.ts";
|
|||||||
import {derived} from "svelte/store";
|
import {derived} from "svelte/store";
|
||||||
|
|
||||||
export class DataRepo {
|
export class DataRepo {
|
||||||
constructor(private token: string) {}
|
constructor(private token: string) {
|
||||||
|
}
|
||||||
|
|
||||||
public async getServer(): Promise<Server> {
|
public async getServer(): Promise<Server> {
|
||||||
return await fetchWithToken(this.token, "/data/server").then(value => value.json()).then(ServerSchema.parse);
|
return await fetchWithToken(this.token, "/data/server").then(value => value.json()).then(ServerSchema.parse);
|
||||||
|
@ -25,24 +25,25 @@ import type {Dayjs} from "dayjs";
|
|||||||
import {derived} from "svelte/store";
|
import {derived} from "svelte/store";
|
||||||
|
|
||||||
export interface CreateEvent {
|
export interface CreateEvent {
|
||||||
name: string
|
name: string;
|
||||||
start: Dayjs
|
start: Dayjs;
|
||||||
end: Dayjs
|
end: Dayjs;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateEvent {
|
export interface UpdateEvent {
|
||||||
name: string
|
name: string;
|
||||||
start: Dayjs
|
start: Dayjs;
|
||||||
end: Dayjs
|
end: Dayjs;
|
||||||
deadline: Dayjs
|
deadline: Dayjs;
|
||||||
maxTeamMembers: number
|
maxTeamMembers: number;
|
||||||
schemType: string | null
|
schemType: string | null;
|
||||||
publicSchemsOnly: boolean
|
publicSchemsOnly: boolean;
|
||||||
spectateSystem: boolean
|
spectateSystem: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EventRepo {
|
export class EventRepo {
|
||||||
constructor(private token: string) {}
|
constructor(private token: string) {
|
||||||
|
}
|
||||||
|
|
||||||
public async listEvents(): Promise<ShortEvent[]> {
|
public async listEvents(): Promise<ShortEvent[]> {
|
||||||
return await fetchWithToken(this.token, "/events")
|
return await fetchWithToken(this.token, "/events")
|
||||||
@ -62,7 +63,7 @@ export class EventRepo {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
name: event.name,
|
name: event.name,
|
||||||
start: +event.start,
|
start: +event.start,
|
||||||
end: +event.end
|
end: +event.end,
|
||||||
}),
|
}),
|
||||||
}).then(value => value.json())
|
}).then(value => value.json())
|
||||||
.then(SWEventSchema.parse);
|
.then(SWEventSchema.parse);
|
||||||
@ -79,18 +80,18 @@ export class EventRepo {
|
|||||||
maxTeamMembers: event.maxTeamMembers,
|
maxTeamMembers: event.maxTeamMembers,
|
||||||
schemType: event.schemType,
|
schemType: event.schemType,
|
||||||
publicSchemsOnly: event.publicSchemsOnly,
|
publicSchemsOnly: event.publicSchemsOnly,
|
||||||
spectateSystem: event.spectateSystem
|
spectateSystem: event.spectateSystem,
|
||||||
}),
|
}),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
}).then(value => value.json())
|
}).then(value => value.json())
|
||||||
.then(SWEventSchema.parse);
|
.then(SWEventSchema.parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteEvent(id: string): Promise<boolean> {
|
public async deleteEvent(id: string): Promise<boolean> {
|
||||||
const res = await fetchWithToken(this.token, `/events/${id}`, {
|
const res = await fetchWithToken(this.token, `/events/${id}`, {
|
||||||
method: "DELETE"
|
method: "DELETE",
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.ok;
|
return res.ok;
|
||||||
|
@ -25,27 +25,28 @@ import type {Dayjs} from "dayjs";
|
|||||||
import {derived} from "svelte/store";
|
import {derived} from "svelte/store";
|
||||||
|
|
||||||
export interface CreateFight {
|
export interface CreateFight {
|
||||||
spielmodus: string
|
spielmodus: string;
|
||||||
map: string
|
map: string;
|
||||||
blueTeam: number
|
blueTeam: number;
|
||||||
redTeam: number
|
redTeam: number;
|
||||||
start: Dayjs
|
start: Dayjs;
|
||||||
kampfleiter: number | null
|
kampfleiter: number | null;
|
||||||
group: string | null
|
group: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateFight {
|
export interface UpdateFight {
|
||||||
spielmodus: string | null
|
spielmodus: string | null;
|
||||||
map: string | null
|
map: string | null;
|
||||||
blueTeam: number | null
|
blueTeam: number | null;
|
||||||
redTeam: number | null
|
redTeam: number | null;
|
||||||
start: Dayjs | null
|
start: Dayjs | null;
|
||||||
kampfleiter: number | null
|
kampfleiter: number | null;
|
||||||
group: string | null
|
group: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FightRepo {
|
export class FightRepo {
|
||||||
constructor(private token: string) {}
|
constructor(private token: string) {
|
||||||
|
}
|
||||||
|
|
||||||
public async listFights(eventId: number): Promise<EventFight[]> {
|
public async listFights(eventId: number): Promise<EventFight[]> {
|
||||||
return await fetchWithToken(this.token, `/events/${eventId}/fights`)
|
return await fetchWithToken(this.token, `/events/${eventId}/fights`)
|
||||||
@ -54,7 +55,7 @@ export class FightRepo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async createFight(eventId: number, fight: CreateFight): Promise<EventFight> {
|
public async createFight(eventId: number, fight: CreateFight): Promise<EventFight> {
|
||||||
return await fetchWithToken(this.token, `/fights`, {
|
return await fetchWithToken(this.token, "/fights", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
event: eventId,
|
event: eventId,
|
||||||
@ -64,10 +65,10 @@ export class FightRepo {
|
|||||||
redTeam: fight.redTeam,
|
redTeam: fight.redTeam,
|
||||||
start: +fight.start,
|
start: +fight.start,
|
||||||
kampfleiter: fight.kampfleiter,
|
kampfleiter: fight.kampfleiter,
|
||||||
group: fight.group
|
group: fight.group,
|
||||||
})
|
}),
|
||||||
}).then(value => value.json())
|
}).then(value => value.json())
|
||||||
.then(EventFightSchema.parse)
|
.then(EventFightSchema.parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateFight(fightId: number, fight: UpdateFight): Promise<EventFight> {
|
public async updateFight(fightId: number, fight: UpdateFight): Promise<EventFight> {
|
||||||
@ -80,15 +81,15 @@ export class FightRepo {
|
|||||||
redTeam: fight.redTeam,
|
redTeam: fight.redTeam,
|
||||||
start: fight.start?.valueOf(),
|
start: fight.start?.valueOf(),
|
||||||
kampfleiter: fight.kampfleiter,
|
kampfleiter: fight.kampfleiter,
|
||||||
group: fight.group
|
group: fight.group,
|
||||||
})
|
}),
|
||||||
}).then(value => value.json())
|
}).then(value => value.json())
|
||||||
.then(EventFightSchema.parse)
|
.then(EventFightSchema.parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteFight(fightId: number): Promise<void> {
|
public async deleteFight(fightId: number): Promise<void> {
|
||||||
const res = await fetchWithToken(this.token, `/fights/${fightId}`, {
|
const res = await fetchWithToken(this.token, `/fights/${fightId}`, {
|
||||||
method: "DELETE"
|
method: "DELETE",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
@ -25,7 +25,8 @@ import {z} from "zod";
|
|||||||
import {derived} from "svelte/store";
|
import {derived} from "svelte/store";
|
||||||
|
|
||||||
export class PageRepo {
|
export class PageRepo {
|
||||||
constructor(private token: string) {}
|
constructor(private token: string) {
|
||||||
|
}
|
||||||
|
|
||||||
public async listPages(branch: string = "master"): Promise<PageList> {
|
public async listPages(branch: string = "master"): Promise<PageList> {
|
||||||
return await fetchWithToken(this.token, `/page?branch=${branch}`)
|
return await fetchWithToken(this.token, `/page?branch=${branch}`)
|
||||||
@ -45,8 +46,8 @@ export class PageRepo {
|
|||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
content: bytesToBase64(new TextEncoder().encode(content)),
|
content: bytesToBase64(new TextEncoder().encode(content)),
|
||||||
sha, message
|
sha, message,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,11 +70,17 @@ export class PageRepo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async merge(branch: string, message: string): Promise<void> {
|
public async merge(branch: string, message: string): Promise<void> {
|
||||||
await fetchWithToken(this.token, "/page/branch/merge", {method: "POST", body: JSON.stringify({branch, message})});
|
await fetchWithToken(this.token, "/page/branch/merge", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({branch, message}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deletePage(id: number, message: string, sha: string, branch: string = "master"): Promise<void> {
|
public async deletePage(id: number, message: string, sha: string, branch: string = "master"): Promise<void> {
|
||||||
await fetchWithToken(this.token, `/page/${id}?branch=${branch}`, {method: "DELETE", body: JSON.stringify({message, sha})});
|
await fetchWithToken(this.token, `/page/${id}?branch=${branch}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
body: JSON.stringify({message, sha}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,8 @@ import {PermsSchema, UserPermsSchema} from "@type/perms.js";
|
|||||||
import {derived} from "svelte/store";
|
import {derived} from "svelte/store";
|
||||||
|
|
||||||
export class PermsRepo {
|
export class PermsRepo {
|
||||||
constructor(private token: string) {}
|
constructor(private token: string) {
|
||||||
|
}
|
||||||
|
|
||||||
public async listPerms(): Promise<Perms> {
|
public async listPerms(): Promise<Perms> {
|
||||||
const res = await fetchWithToken(this.token, "/perms");
|
const res = await fetchWithToken(this.token, "/perms");
|
||||||
|
@ -20,7 +20,12 @@
|
|||||||
import {writable} from "svelte/store";
|
import {writable} from "svelte/store";
|
||||||
|
|
||||||
export const fetchWithToken = (token: string, url: string, params: RequestInit = {}) =>
|
export const fetchWithToken = (token: string, url: string, params: RequestInit = {}) =>
|
||||||
fetch(`${import.meta.env.PUBLIC_API_SERVER}${url}`, {...params, headers: {...(token !== "" ? {"Authorization": "Bearer " + (token)}:{}), "Content-Type": "application/json", ...params.headers}})
|
fetch(`${import.meta.env.PUBLIC_API_SERVER}${url}`, {...params,
|
||||||
|
headers: {
|
||||||
|
...(token !== "" ? {"Authorization": "Bearer " + (token)} : {}),
|
||||||
|
"Content-Type": "application/json", ...params.headers,
|
||||||
|
},
|
||||||
|
})
|
||||||
.then(value => {
|
.then(value => {
|
||||||
if (value.status === 401) {
|
if (value.status === 401) {
|
||||||
tokenStore.set("");
|
tokenStore.set("");
|
||||||
|
@ -18,12 +18,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {fetchWithToken, tokenStore} from "./repo.ts";
|
import {fetchWithToken, tokenStore} from "./repo.ts";
|
||||||
import type {SchematicCode, SchematicInfo, SchematicList} from "@type/schem.ts";
|
import type {SchematicInfo, SchematicList} from "@type/schem.ts";
|
||||||
import {SchematicCodeSchema, SchematicInfoSchema, SchematicListSchema} from "@type/schem.ts";
|
import {SchematicInfoSchema, SchematicListSchema} from "@type/schem.ts";
|
||||||
import {derived} from "svelte/store";
|
import {derived} from "svelte/store";
|
||||||
|
|
||||||
export class SchematicRepo {
|
export class SchematicRepo {
|
||||||
constructor(private token: string) {}
|
constructor(private token: string) {
|
||||||
|
}
|
||||||
|
|
||||||
public async getRootSchematicList(): Promise<SchematicList> {
|
public async getRootSchematicList(): Promise<SchematicList> {
|
||||||
return await fetchWithToken(this.token, "/schem").then(value => value.json()).then(SchematicListSchema.parse);
|
return await fetchWithToken(this.token, "/schem").then(value => value.json()).then(SchematicListSchema.parse);
|
||||||
@ -42,8 +43,8 @@ export class SchematicRepo {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
name,
|
name,
|
||||||
content
|
content,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,8 @@ import {derived} from "svelte/store";
|
|||||||
|
|
||||||
export class StatsRepo {
|
export class StatsRepo {
|
||||||
|
|
||||||
constructor(private token: string) {}
|
constructor(private token: string) {
|
||||||
|
}
|
||||||
|
|
||||||
public async getRankings(gamemode: string): Promise<Ranking> {
|
public async getRankings(gamemode: string): Promise<Ranking> {
|
||||||
return await fetchWithToken(this.token, `/stats/ranked/${gamemode}`).then(value => value.json()).then(RankingSchema.parse);
|
return await fetchWithToken(this.token, `/stats/ranked/${gamemode}`).then(value => value.json()).then(RankingSchema.parse);
|
||||||
|
@ -21,7 +21,7 @@ import {readonly, writable} from "svelte/store";
|
|||||||
|
|
||||||
import type {Readable, Subscriber, Unsubscriber} from "svelte/store";
|
import type {Readable, Subscriber, Unsubscriber} from "svelte/store";
|
||||||
|
|
||||||
export interface Cached<T> extends Readable<T>{
|
export interface Cached<T> extends Readable<T> {
|
||||||
reload: () => void;
|
reload: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,20 +38,20 @@ export function cached<T>(normal: T, init: () => Promise<T>): Cached<T> {
|
|||||||
return {
|
return {
|
||||||
...readonly(store),
|
...readonly(store),
|
||||||
subscribe: (run: Subscriber<T>, invalidate?: (value?: T) => void): Unsubscriber => {
|
subscribe: (run: Subscriber<T>, invalidate?: (value?: T) => void): Unsubscriber => {
|
||||||
if(first) {
|
if (first) {
|
||||||
first = false;
|
first = false;
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
return store.subscribe(run, invalidate);
|
return store.subscribe(run, invalidate);
|
||||||
},
|
},
|
||||||
reload
|
reload,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cachedFamily<T, K>(normal: K, init: (arg0: T) => Promise<K>): (arg: T) => Cached<K> {
|
export function cachedFamily<T, K>(normal: K, init: (arg0: T) => Promise<K>): (arg: T) => Cached<K> {
|
||||||
const stores: Map<T, Cached<K>> = new Map();
|
const stores: Map<T, Cached<K>> = new Map();
|
||||||
return (arg: T) => {
|
return (arg: T) => {
|
||||||
if(stores.has(arg)) {
|
if (stores.has(arg)) {
|
||||||
return stores.get(arg)!;
|
return stores.get(arg)!;
|
||||||
} else {
|
} else {
|
||||||
const store = writable<K>(normal);
|
const store = writable<K>(normal);
|
||||||
@ -66,13 +66,13 @@ export function cachedFamily<T, K>(normal: K, init: (arg0: T) => Promise<K>): (a
|
|||||||
const cachedStore = {
|
const cachedStore = {
|
||||||
...readonly(store),
|
...readonly(store),
|
||||||
subscribe: (run: Subscriber<K>, invalidate?: (value?: K) => void): Unsubscriber => {
|
subscribe: (run: Subscriber<K>, invalidate?: (value?: K) => void): Unsubscriber => {
|
||||||
if(first) {
|
if (first) {
|
||||||
first = false;
|
first = false;
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
return store.subscribe(run, invalidate);
|
return store.subscribe(run, invalidate);
|
||||||
},
|
},
|
||||||
reload
|
reload,
|
||||||
} as Cached<K>;
|
} as Cached<K>;
|
||||||
|
|
||||||
stores.set(arg, cachedStore);
|
stores.set(arg, cachedStore);
|
||||||
|
@ -41,13 +41,13 @@ export const ServerSchema = z.object({
|
|||||||
players: z.object({
|
players: z.object({
|
||||||
online: z.number(),
|
online: z.number(),
|
||||||
max: z.number(),
|
max: z.number(),
|
||||||
sample: z.array(z.any()).optional()
|
sample: z.array(z.any()).optional(),
|
||||||
}),
|
}),
|
||||||
version: z.object({
|
version: z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
protocol: z.number()
|
protocol: z.number(),
|
||||||
}),
|
}),
|
||||||
favicon: z.string().optional()
|
favicon: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Server = z.infer<typeof ServerSchema>;
|
export type Server = z.infer<typeof ServerSchema>;
|
||||||
|
@ -24,7 +24,7 @@ export const ListPageSchema = z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
sha: z.string(),
|
sha: z.string(),
|
||||||
downloadUrl: z.string().url(),
|
downloadUrl: z.string().url(),
|
||||||
id: z.number().positive()
|
id: z.number().positive(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ListPage = z.infer<typeof ListPageSchema>;
|
export type ListPage = z.infer<typeof ListPageSchema>;
|
||||||
@ -40,12 +40,12 @@ export const PageSchema = z.object({
|
|||||||
downloadUrl: z.string().url(),
|
downloadUrl: z.string().url(),
|
||||||
content: z.string(),
|
content: z.string(),
|
||||||
size: z.number().gte(0),
|
size: z.number().gte(0),
|
||||||
id: z.number().positive()
|
id: z.number().positive(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Page = z.infer<typeof PageSchema>;
|
export type Page = z.infer<typeof PageSchema>;
|
||||||
|
|
||||||
export type Pages = {
|
export type Pages = {
|
||||||
dirs: {[key: string]: Pages},
|
dirs: { [key: string]: Pages },
|
||||||
files: {[key: string]: ListPage}
|
files: { [key: string]: ListPage }
|
||||||
}
|
}
|
@ -22,7 +22,7 @@ import {z} from "zod";
|
|||||||
export const PrefixSchema = z.object({
|
export const PrefixSchema = z.object({
|
||||||
name: z.string().startsWith("PREFIX_"),
|
name: z.string().startsWith("PREFIX_"),
|
||||||
colorCode: z.string().length(2).startsWith("§"),
|
colorCode: z.string().length(2).startsWith("§"),
|
||||||
chatPrefix: z.string()
|
chatPrefix: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Prefix = z.infer<typeof PrefixSchema>;
|
export type Prefix = z.infer<typeof PrefixSchema>;
|
||||||
|
@ -29,7 +29,7 @@ export const SchematicSchema = z.object({
|
|||||||
lastUpdate: z.number().positive(),
|
lastUpdate: z.number().positive(),
|
||||||
rank: z.number(),
|
rank: z.number(),
|
||||||
replaceColor: z.boolean(),
|
replaceColor: z.boolean(),
|
||||||
allowReplay: z.boolean()
|
allowReplay: z.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Schematic = z.infer<typeof SchematicSchema>
|
export type Schematic = z.infer<typeof SchematicSchema>
|
||||||
@ -37,10 +37,10 @@ export type Schematic = z.infer<typeof SchematicSchema>
|
|||||||
export const SchematicListSchema = z.object({
|
export const SchematicListSchema = z.object({
|
||||||
breadcrumbs: z.array(z.object({
|
breadcrumbs: z.array(z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
id: z.number()
|
id: z.number(),
|
||||||
})),
|
})),
|
||||||
schematics: z.array(SchematicSchema),
|
schematics: z.array(SchematicSchema),
|
||||||
players: z.record(z.string(), PlayerSchema)
|
players: z.record(z.string(), PlayerSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type SchematicList = z.infer<typeof SchematicListSchema>
|
export type SchematicList = z.infer<typeof SchematicListSchema>
|
||||||
@ -48,7 +48,7 @@ export type SchematicList = z.infer<typeof SchematicListSchema>
|
|||||||
export const SchematicInfoSchema = z.object({
|
export const SchematicInfoSchema = z.object({
|
||||||
members: z.array(PlayerSchema),
|
members: z.array(PlayerSchema),
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
schem: SchematicSchema
|
schem: SchematicSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type SchematicInfo = z.infer<typeof SchematicInfoSchema>
|
export type SchematicInfo = z.infer<typeof SchematicInfoSchema>
|
||||||
@ -56,7 +56,7 @@ export type SchematicInfo = z.infer<typeof SchematicInfoSchema>
|
|||||||
export const SchematicCodeSchema = z.object({
|
export const SchematicCodeSchema = z.object({
|
||||||
id: z.number().gte(0),
|
id: z.number().gte(0),
|
||||||
code: z.string(),
|
code: z.string(),
|
||||||
expires: z.number().positive()
|
expires: z.number().positive(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type SchematicCode = z.infer<typeof SchematicCodeSchema>
|
export type SchematicCode = z.infer<typeof SchematicCodeSchema>
|
@ -39,7 +39,7 @@ export const UserStatsSchema = z.object({
|
|||||||
eventParticipation: z.number(),
|
eventParticipation: z.number(),
|
||||||
acceptedSchematics: z.number(),
|
acceptedSchematics: z.number(),
|
||||||
fights: z.number(),
|
fights: z.number(),
|
||||||
playtime: z.number()
|
playtime: z.number(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UserStats = z.infer<typeof UserStatsSchema>;
|
export type UserStats = z.infer<typeof UserStatsSchema>;
|
@ -17,19 +17,18 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import {defineCollection, reference, z} from "astro:content";
|
import {defineCollection, reference, z} from "astro:content";
|
||||||
|
|
||||||
export const pagesSchema = z.object({
|
export const pagesSchema = z.object({
|
||||||
title: z.string().min(1).max(80),
|
title: z.string().min(1).max(80),
|
||||||
description: z.string().min(1).max(120),
|
description: z.string().min(1).max(120),
|
||||||
german: z.boolean().optional().default(false),
|
german: z.boolean().optional().default(false),
|
||||||
image: z.string().optional()
|
image: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const pages = defineCollection({
|
export const pages = defineCollection({
|
||||||
type: "content",
|
type: "content",
|
||||||
schema: pagesSchema
|
schema: pagesSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const help = defineCollection({
|
export const help = defineCollection({
|
||||||
@ -38,8 +37,8 @@ export const help = defineCollection({
|
|||||||
title: z.string().min(1).max(80),
|
title: z.string().min(1).max(80),
|
||||||
description: z.string().min(1).max(120),
|
description: z.string().min(1).max(120),
|
||||||
tags: z.array(z.string()),
|
tags: z.array(z.string()),
|
||||||
related: z.array(reference("help")).optional()
|
related: z.array(reference("help")).optional(),
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const modes = defineCollection({
|
export const modes = defineCollection({
|
||||||
@ -47,8 +46,8 @@ export const modes = defineCollection({
|
|||||||
schema: z.object({
|
schema: z.object({
|
||||||
translationKey: z.string(),
|
translationKey: z.string(),
|
||||||
main: z.boolean(),
|
main: z.boolean(),
|
||||||
ranked: z.boolean().optional().default(false)
|
ranked: z.boolean().optional().default(false),
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const downloads = defineCollection({
|
export const downloads = defineCollection({
|
||||||
@ -59,14 +58,14 @@ export const downloads = defineCollection({
|
|||||||
url: z.string().url()
|
url: z.string().url()
|
||||||
.or(z.record(z.string(), z.string())),
|
.or(z.record(z.string(), z.string())),
|
||||||
sourceUrl: z.string().url().optional(),
|
sourceUrl: z.string().url().optional(),
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const rules = defineCollection({
|
export const rules = defineCollection({
|
||||||
type: "content",
|
type: "content",
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
translationKey: z.string(),
|
translationKey: z.string(),
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const announcements = defineCollection({
|
export const announcements = defineCollection({
|
||||||
@ -77,8 +76,8 @@ export const announcements = defineCollection({
|
|||||||
image: image().optional(),
|
image: image().optional(),
|
||||||
tags: z.array(z.string()),
|
tags: z.array(z.string()),
|
||||||
created: z.date(),
|
created: z.date(),
|
||||||
key: z.string()
|
key: z.string(),
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const collections = {
|
export const collections = {
|
||||||
@ -87,5 +86,5 @@ export const collections = {
|
|||||||
"modes": modes,
|
"modes": modes,
|
||||||
"rules": rules,
|
"rules": rules,
|
||||||
"downloads": downloads,
|
"downloads": downloads,
|
||||||
"announcements": announcements
|
"announcements": announcements,
|
||||||
};
|
};
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
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;
|
const {title, description} = Astro.props.frontmatter || Astro.props;
|
||||||
import { SEO } from "astro-seo";
|
import {SEO} from "astro-seo";
|
||||||
|
|
||||||
import "../../public/fonts/roboto/roboto.css";
|
import "../../public/fonts/roboto/roboto.css";
|
||||||
|
|
||||||
@ -17,24 +17,24 @@ const iconImage = await getImage({src: icon, height: 32, width: 32, format: "png
|
|||||||
content="width=device-width, user-scalable=5, initial-scale=1.0, maximum-scale=5.0, minimum-scale=1.0">
|
content="width=device-width, user-scalable=5, initial-scale=1.0, maximum-scale=5.0, minimum-scale=1.0">
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
<meta name="icbm" content="52.370216;4.895168"/>
|
<meta name="icbm" content="52.370216;4.895168"/>
|
||||||
<link rel="icon" type="imgage/png" href={iconImage.src} />
|
<link rel="icon" type="imgage/png" href={iconImage.src}/>
|
||||||
|
|
||||||
<SEO
|
<SEO
|
||||||
title={title}
|
title={title}
|
||||||
description={description}
|
description={description}
|
||||||
twitter={{
|
twitter={{
|
||||||
creator: "@chaoscaot"
|
creator: "@chaoscaot",
|
||||||
}}
|
}}
|
||||||
languageAlternates={astroI18n.locales.map((locale) => ({
|
languageAlternates={astroI18n.locales.map((locale) => ({
|
||||||
hrefLang: locale,
|
hrefLang: locale,
|
||||||
href: `/${locale}/`
|
href: `/${locale}/`,
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<slot name="head" />
|
<slot name="head"/>
|
||||||
</head>
|
</head>
|
||||||
<body class="dark:bg-zinc-800">
|
<body class="dark:bg-zinc-800">
|
||||||
<slot />
|
<slot/>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import { Image } from "astro:assets";
|
import {Image} from "astro:assets";
|
||||||
import Basic from "./Basic.astro";
|
import Basic from "./Basic.astro";
|
||||||
import "../styles/button.css";
|
import "../styles/button.css";
|
||||||
import localLogo from "../images/logo.png";
|
import localLogo from "../images/logo.png";
|
||||||
@ -9,18 +9,19 @@ import {l} from "../util/util";
|
|||||||
|
|
||||||
import ServerStatus from "../components/ServerStatus.svelte";
|
import ServerStatus from "../components/ServerStatus.svelte";
|
||||||
|
|
||||||
const { title, description } = Astro.props;
|
const {title, description} = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<Basic title={title} description={description}>
|
<Basic title={title} description={description}>
|
||||||
<slot name="head" slot="head" />
|
<slot name="head" slot="head"/>
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div class="min-h-screen flex flex-col">
|
<div class="min-h-screen flex flex-col">
|
||||||
<nav-bar class="fixed top-0 left-0 right-0 px-4 transition-colors z-10 flex justify-center \
|
<nav-bar class="fixed top-0 left-0 right-0 px-4 transition-colors z-10 flex justify-center \
|
||||||
before:bg-black before:absolute before:top-0 before:left-0 before:bottom-0 before:right-0 before:-z-10 before:scale-y-0 before:transition-transform before:origin-top">
|
before:bg-black before:absolute before:top-0 before:left-0 before:bottom-0 before:right-0 before:-z-10 before:scale-y-0 before:transition-transform before:origin-top">
|
||||||
<div class="flex flex-col md:flex-row items-center justify-evenly md:justify-between match">
|
<div class="flex flex-col md:flex-row items-center justify-evenly md:justify-between match">
|
||||||
<a class="flex items-center" href={l("/")}>
|
<a class="flex items-center" href={l("/")}>
|
||||||
<Image src={localLogo} alt={t("navbar.logo.alt")} width="44" height="44" quality="max" class="mr-2 p-1 bg-black rounded-full" />
|
<Image src={localLogo} alt={t("navbar.logo.alt")} width="44" height="44" quality="max"
|
||||||
|
class="mr-2 p-1 bg-black rounded-full"/>
|
||||||
<h1 class="text-2xl uppercase font-bold inline-block dark:text-white">
|
<h1 class="text-2xl uppercase font-bold inline-block dark:text-white">
|
||||||
{t("navbar.title")}
|
{t("navbar.title")}
|
||||||
</h1>
|
</h1>
|
||||||
@ -31,10 +32,11 @@ const { title, description } = Astro.props;
|
|||||||
<a href={l("/")}>
|
<a href={l("/")}>
|
||||||
<span class="btn__text">{t("navbar.links.home.title")}</span>
|
<span class="btn__text">{t("navbar.links.home.title")}</span>
|
||||||
</a>
|
</a>
|
||||||
<CaretDownOutline class="ml-2 mt-auto" />
|
<CaretDownOutline class="ml-2 mt-auto"/>
|
||||||
</button>
|
</button>
|
||||||
<div>
|
<div>
|
||||||
<a class="btn btn-gray my-1" href={l("/announcements")}>{t("navbar.links.home.announcements")}</a>
|
<a class="btn btn-gray my-1"
|
||||||
|
href={l("/announcements")}>{t("navbar.links.home.announcements")}</a>
|
||||||
<a class="btn btn-gray" href={l("/about")}>{t("navbar.links.home.about")}</a>
|
<a class="btn btn-gray" href={l("/about")}>{t("navbar.links.home.about")}</a>
|
||||||
<a class="btn btn-gray" href={l("/downloads")}>{t("navbar.links.home.downloads")}</a>
|
<a class="btn btn-gray" href={l("/downloads")}>{t("navbar.links.home.downloads")}</a>
|
||||||
<a class="btn btn-gray" href={l("/faq")}>{t("navbar.links.home.faq")}</a>
|
<a class="btn btn-gray" href={l("/faq")}>{t("navbar.links.home.faq")}</a>
|
||||||
@ -45,7 +47,7 @@ const { title, description } = Astro.props;
|
|||||||
<a rel="prefetch" href={l("/rules")}>
|
<a rel="prefetch" href={l("/rules")}>
|
||||||
<span class="btn__text">{t("navbar.links.rules.title")}</span>
|
<span class="btn__text">{t("navbar.links.rules.title")}</span>
|
||||||
</a>
|
</a>
|
||||||
<CaretDownOutline class="ml-2 mt-auto" />
|
<CaretDownOutline class="ml-2 mt-auto"/>
|
||||||
</button>
|
</button>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="px-2 text-gray-300">{t("navbar.links.rules.gamemode")}</h2>
|
<h2 class="px-2 text-gray-300">{t("navbar.links.rules.gamemode")}</h2>
|
||||||
@ -55,9 +57,11 @@ const { title, description } = Astro.props;
|
|||||||
<a href={l("/rules/airship")} class="btn btn-gray">{t("navbar.links.rules.as")}</a>
|
<a href={l("/rules/airship")} class="btn btn-gray">{t("navbar.links.rules.as")}</a>
|
||||||
<a href={l("/rules/quickgear")} class="btn btn-gray">{t("navbar.links.rules.qg")}</a>
|
<a href={l("/rules/quickgear")} class="btn btn-gray">{t("navbar.links.rules.qg")}</a>
|
||||||
<h2 class="px-2 text-gray-300">{t("navbar.links.rules.rotating")}</h2>
|
<h2 class="px-2 text-gray-300">{t("navbar.links.rules.rotating")}</h2>
|
||||||
<a href={l("/rules/megawargear")}class="btn btn-gray">{t("navbar.links.rules.megawg")}</a>
|
<a href={l("/rules/megawargear")}
|
||||||
<a href={l("/rules/microwargear")}class="btn btn-gray">{t("navbar.links.rules.micro")}</a>
|
class="btn btn-gray">{t("navbar.links.rules.megawg")}</a>
|
||||||
<a href={l("/rules/streetfight")}class="btn btn-gray">{t("navbar.links.rules.sf")}</a>
|
<a href={l("/rules/microwargear")}
|
||||||
|
class="btn btn-gray">{t("navbar.links.rules.micro")}</a>
|
||||||
|
<a href={l("/rules/streetfight")} class="btn btn-gray">{t("navbar.links.rules.sf")}</a>
|
||||||
<h2 class="px-2 text-gray-300">{t("navbar.links.rules.general")}</h2>
|
<h2 class="px-2 text-gray-300">{t("navbar.links.rules.general")}</h2>
|
||||||
<a class="btn btn-gray" href={l("/code-of-conduct")}>{t("navbar.links.rules.coc")}</a>
|
<a class="btn btn-gray" href={l("/code-of-conduct")}>{t("navbar.links.rules.coc")}</a>
|
||||||
</div>
|
</div>
|
||||||
@ -83,7 +87,7 @@ const { title, description } = Astro.props;
|
|||||||
</div>
|
</div>
|
||||||
</nav-bar>
|
</nav-bar>
|
||||||
<script>
|
<script>
|
||||||
class Navbar extends HTMLElement {
|
class Navbar extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
if (window.scrollY != 0) {
|
if (window.scrollY != 0) {
|
||||||
@ -100,18 +104,18 @@ class Navbar extends HTMLElement {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("nav-bar", Navbar);
|
customElements.define("nav-bar", Navbar);
|
||||||
</script>
|
</script>
|
||||||
<main class="flex-1">
|
<main class="flex-1">
|
||||||
<slot />
|
<slot/>
|
||||||
</main>
|
</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">
|
<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 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 client:only="svelte"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-card">
|
<div class="footer-card">
|
||||||
<h1>Links</h1>
|
<h1>Links</h1>
|
||||||
@ -126,8 +130,12 @@ customElements.define("nav-bar", Navbar);
|
|||||||
</div>
|
</div>
|
||||||
<div class="footer-card">
|
<div class="footer-card">
|
||||||
<h1>Social Media</h1>
|
<h1>Social Media</h1>
|
||||||
<a class="flex" href="/youtube"><YoutubeSolid class="mr-2" /> YouTube</a>
|
<a class="flex" href="/youtube">
|
||||||
<a class="flex" href="/discord"><DiscordSolid class="mr-2" /> Discord</a>
|
<YoutubeSolid class="mr-2"/>
|
||||||
|
YouTube</a>
|
||||||
|
<a class="flex" href="/discord">
|
||||||
|
<DiscordSolid class="mr-2"/>
|
||||||
|
Discord</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm text-white text-center mt-1">© SteamWar.de - Made with ❤️ by Chaoscaot</span>
|
<span class="text-sm text-white text-center mt-1">© SteamWar.de - Made with ❤️ by Chaoscaot</span>
|
||||||
@ -139,10 +147,12 @@ customElements.define("nav-bar", Navbar);
|
|||||||
<style>
|
<style>
|
||||||
.footer-card {
|
.footer-card {
|
||||||
@apply w-40 text-gray-400 flex flex-col;
|
@apply w-40 text-gray-400 flex flex-col;
|
||||||
>h1 {
|
|
||||||
|
> h1 {
|
||||||
@apply text-xl font-bold text-gray-100;
|
@apply text-xl font-bold text-gray-100;
|
||||||
}
|
}
|
||||||
>a {
|
|
||||||
|
> a {
|
||||||
@apply hover:underline;
|
@apply hover:underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
---
|
---
|
||||||
import NavbarLayout from "./NavbarLayout.astro";
|
import NavbarLayout from "./NavbarLayout.astro";
|
||||||
import localBau from "../images/2023-10-08_20.43.43.png";
|
import localBau from "../images/2023-10-08_20.43.43.png";
|
||||||
import { Image } from "astro:assets";
|
import {Image} from "astro:assets";
|
||||||
|
|
||||||
const { title, description, image } = Astro.props;
|
const {title, description, image} = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<NavbarLayout title={title} description={description}>
|
<NavbarLayout title={title} description={description}>
|
||||||
<slot name="head" slot="head" />
|
<slot name="head" slot="head"/>
|
||||||
<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" />
|
<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
|
<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(100vw, 75em);">
|
||||||
<slot />
|
<slot/>
|
||||||
</div>
|
</div>
|
||||||
</NavbarLayout>
|
</NavbarLayout>
|
@ -17,31 +17,31 @@ export const getStaticPaths = createGetStaticPaths(async () => {
|
|||||||
|
|
||||||
return posts.map((page) => ({
|
return posts.map((page) => ({
|
||||||
props: {
|
props: {
|
||||||
page
|
page,
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
slug: fixLink(page.slug)
|
slug: fixLink(page.slug),
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
const { page } = Astro.props;
|
const {page} = Astro.props;
|
||||||
const { Content } = await page.render();
|
const {Content} = await page.render();
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout title={page.data.title}>
|
<PageLayout title={page.data.title}>
|
||||||
<article>
|
<article>
|
||||||
{page.data.german && (
|
{page.data.german && (
|
||||||
<LanguageWarning />
|
<LanguageWarning/>
|
||||||
)}
|
)}
|
||||||
<h1 class="text-left">{page.data.title}</h1>
|
<h1 class="text-left">{page.data.title}</h1>
|
||||||
<Content />
|
<Content/>
|
||||||
</article>
|
</article>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
|
|
||||||
<style is:global>
|
<style is:global>
|
||||||
article {
|
article {
|
||||||
>* {
|
> * {
|
||||||
all: revert;
|
all: revert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,5 +4,5 @@ import Basic from "../../layouts/Basic.astro";
|
|||||||
---
|
---
|
||||||
|
|
||||||
<Basic>
|
<Basic>
|
||||||
<App client:only="svelte" />
|
<App client:only="svelte"/>
|
||||||
</Basic>
|
</Basic>
|
@ -5,7 +5,7 @@ import PageLayout from "@layouts/PageLayout.astro";
|
|||||||
import {TagSolid, CalendarMonthSolid} from "flowbite-svelte-icons";
|
import {TagSolid, CalendarMonthSolid} from "flowbite-svelte-icons";
|
||||||
import TagComponent from "@components/TagComponent.astro";
|
import TagComponent from "@components/TagComponent.astro";
|
||||||
import LanguageWarning from "@components/LanguageWarning.astro";
|
import LanguageWarning from "@components/LanguageWarning.astro";
|
||||||
import { SEO } from "astro-seo";
|
import {SEO} from "astro-seo";
|
||||||
import localBau from "@images/2022-03-28_13.18.25.png";
|
import localBau from "@images/2022-03-28_13.18.25.png";
|
||||||
import {getImage, Image} from "astro:assets";
|
import {getImage, Image} from "astro:assets";
|
||||||
|
|
||||||
@ -24,12 +24,12 @@ export const getStaticPaths = createGetStaticPaths(async () => {
|
|||||||
|
|
||||||
return posts.map(value => ({
|
return posts.map(value => ({
|
||||||
params: {
|
params: {
|
||||||
slug: value.slug.split("/").slice(1).join("/")
|
slug: value.slug.split("/").slice(1).join("/"),
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
post: value,
|
post: value,
|
||||||
german: value.id.split("/")[0] != astroI18n.locale
|
german: value.id.split("/")[0] != astroI18n.locale,
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {post, german} = Astro.props;
|
const {post, german} = Astro.props;
|
||||||
const { Content } = await post.render();
|
const {Content} = await post.render();
|
||||||
|
|
||||||
const ogImage = await getImage({
|
const ogImage = await getImage({
|
||||||
src: post.data.image || localBau,
|
src: post.data.image || localBau,
|
||||||
@ -47,7 +47,6 @@ const ogImage = await getImage({
|
|||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630,
|
height: 630,
|
||||||
});
|
});
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout title={post.data.title} description={post.data.description}>
|
<PageLayout title={post.data.title} description={post.data.description}>
|
||||||
@ -71,24 +70,29 @@ const ogImage = await getImage({
|
|||||||
<div class={"relative w-full " + (post.data.image ? "aspect-video" : "")}>
|
<div class={"relative w-full " + (post.data.image ? "aspect-video" : "")}>
|
||||||
{post.data.image && (
|
{post.data.image && (
|
||||||
<div class="absolute top-0 left-0 w-full aspect-video flex justify-center">
|
<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" />
|
<Image src={post.data.image} height="1080" alt=""
|
||||||
|
class="rounded-2xl linear-fade object-contain h-full"/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div class={post.data.image ? "absolute bottom-8 left-2" : "mb-4"}>
|
<div class={post.data.image ? "absolute bottom-8 left-2" : "mb-4"}>
|
||||||
<h1 class="text-4xl mb-0">{post.data.title}</h1>
|
<h1 class="text-4xl mb-0">{post.data.title}</h1>
|
||||||
<h5 class="flex items-center mt-2 text-neutral-300"><TagSolid class="w-4 h-4 mr-2" /> {post.data.tags.map(tag => (
|
<h5 class="flex items-center mt-2 text-neutral-300">
|
||||||
<TagComponent tag={tag} />
|
<TagSolid class="w-4 h-4 mr-2"/>
|
||||||
))} <CalendarMonthSolid class="w-4 h-4 mr-2" /> {Intl.DateTimeFormat(astroI18n.locale, {
|
{post.data.tags.map(tag => (
|
||||||
|
<TagComponent tag={tag}/>
|
||||||
|
))}
|
||||||
|
<CalendarMonthSolid class="w-4 h-4 mr-2"/>
|
||||||
|
{Intl.DateTimeFormat(astroI18n.locale, {
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
month: "short",
|
month: "short",
|
||||||
year: "numeric"
|
year: "numeric",
|
||||||
}).format(post.data.created)} </h5>
|
}).format(post.data.created)} </h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{german && (
|
{german && (
|
||||||
<LanguageWarning />
|
<LanguageWarning/>
|
||||||
)}
|
)}
|
||||||
<Content />
|
<Content/>
|
||||||
<script>
|
<script>
|
||||||
import FightTable from "@components/FightTable.svelte";
|
import FightTable from "@components/FightTable.svelte";
|
||||||
// @ts-expect-error Import Schenanigans
|
// @ts-expect-error Import Schenanigans
|
||||||
@ -96,6 +100,7 @@ const ogImage = await getImage({
|
|||||||
import GroupTable from "@components/GroupTable.svelte";
|
import GroupTable from "@components/GroupTable.svelte";
|
||||||
import {eventRepo} from "../../components/repo/event";
|
import {eventRepo} from "../../components/repo/event";
|
||||||
import type {ExtendedEvent} from "@type/event";
|
import type {ExtendedEvent} from "@type/event";
|
||||||
|
|
||||||
const eventMounts: Map<string, ((ev: ExtendedEvent) => void)[]> = new Map();
|
const eventMounts: Map<string, ((ev: ExtendedEvent) => void)[]> = new Map();
|
||||||
|
|
||||||
class FightTableElement extends HTMLElement {
|
class FightTableElement extends HTMLElement {
|
||||||
@ -111,7 +116,7 @@ const ogImage = await getImage({
|
|||||||
event: ev,
|
event: ev,
|
||||||
group: this.dataset["group"],
|
group: this.dataset["group"],
|
||||||
rows: !isNaN(rows) ? rows : 1,
|
rows: !isNaN(rows) ? rows : 1,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -130,7 +135,7 @@ const ogImage = await getImage({
|
|||||||
event: ev,
|
event: ev,
|
||||||
group: this.dataset["group"],
|
group: this.dataset["group"],
|
||||||
rows: !isNaN(rows) ? rows : 1,
|
rows: !isNaN(rows) ? rows : 1,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -160,7 +165,7 @@ const ogImage = await getImage({
|
|||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
>* {
|
> * {
|
||||||
all: revert;
|
all: revert;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,6 +181,6 @@ const ogImage = await getImage({
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.linear-fade {
|
.linear-fade {
|
||||||
mask-image: linear-gradient(to bottom, rgba(0,0,0,1), rgba(0,0,0,1), rgba(0,0,0,1), rgba(0,0,0,0));
|
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 1), rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from "astro:content";
|
import {getCollection} from "astro:content";
|
||||||
import PageLayout from "../../layouts/PageLayout.astro";
|
import PageLayout from "../../layouts/PageLayout.astro";
|
||||||
import {astroI18n, t} from "astro-i18n";
|
import {astroI18n, t} from "astro-i18n";
|
||||||
import PostComponent from "../../components/PostComponent.astro";
|
import PostComponent from "../../components/PostComponent.astro";
|
||||||
@ -22,14 +22,14 @@ async function getPosts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const posts = await getPosts();
|
const posts = await getPosts();
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout title={t("blog.title")}>
|
<PageLayout title={t("blog.title")}>
|
||||||
{posts.map((post, index) => (
|
{posts.map((post, index) => (
|
||||||
<div>
|
<div>
|
||||||
<PostComponent post={post} />
|
<PostComponent post={post}/>
|
||||||
{index !== posts.length - 1 && <hr/>}
|
{index !== posts.length - 1 &&
|
||||||
|
<hr/>}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</PageLayout>
|
</PageLayout>
|
@ -36,28 +36,29 @@ export const getStaticPaths = createGetStaticPaths(async () => {
|
|||||||
|
|
||||||
return Object.keys(groupedByTags).map(tag => ({
|
return Object.keys(groupedByTags).map(tag => ({
|
||||||
params: {
|
params: {
|
||||||
tag: tag
|
tag: tag,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
posts: groupedByTags[tag],
|
posts: groupedByTags[tag],
|
||||||
tag: tag
|
tag: tag,
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
posts: CollectionEntry<"announcements">[]
|
posts: CollectionEntry<"announcements">[];
|
||||||
tag: string
|
tag: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { posts, tag } = Astro.props;
|
const {posts, tag} = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout title={t("tag.title", {tag: capitalize(tag)})}>
|
<PageLayout title={t("tag.title", {tag: capitalize(tag)})}>
|
||||||
{posts.map((post, index) => (
|
{posts.map((post, index) => (
|
||||||
<div>
|
<div>
|
||||||
<PostComponent post={post} />
|
<PostComponent post={post}/>
|
||||||
{index !== posts.length - 1 && <hr/>}
|
{index !== posts.length - 1 &&
|
||||||
|
<hr/>}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</PageLayout>
|
</PageLayout>
|
@ -7,11 +7,12 @@ 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";
|
||||||
|
|
||||||
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");
|
window.location.href = l("/login");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<Dashboard client:only="svelte" />
|
<Dashboard client:only="svelte"/>
|
||||||
</PageLayout>
|
</PageLayout>
|
@ -10,18 +10,21 @@ const downloads = await getCollection("downloads");
|
|||||||
<PageLayout title="Downloads">
|
<PageLayout title="Downloads">
|
||||||
{downloads.map(e => (
|
{downloads.map(e => (
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
<h1 class="font-bold text-6xl">{e.data.name}</h1>
|
<h1 class="font-bold text-2xl">{e.data.name}</h1>
|
||||||
<div class="py-4">{t(e.data.description)}</div>
|
<div class="py-4">{t(e.data.description)}</div>
|
||||||
|
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
{typeof e.data.url === "object" ?
|
{typeof e.data.url === "object" ?
|
||||||
Object.entries(e.data.url).map(value => (
|
Object.entries(e.data.url).map(value => (
|
||||||
<a href={value[1].startsWith("/") ? l(value[1]) : value[1]} class="text-blue-500 hover:underline w-fit">{t(value[0])}</a>
|
<a href={value[1].startsWith("/") ? l(value[1]) : value[1]}
|
||||||
|
class="text-blue-500 hover:underline w-fit">{t(value[0])}</a>
|
||||||
))
|
))
|
||||||
:
|
:
|
||||||
<a href={e.data.url} class="text-blue-500 hover:underline w-fit">{t("Download")}</a>
|
<a href={e.data.url} class="text-blue-500 hover:underline w-fit">{t("Download")}</a>
|
||||||
}
|
}
|
||||||
{e.data.sourceUrl ? <a class="text-blue-500 hover:underline w-fit" href={e.data.sourceUrl}>Quelle</a> : null}
|
{e.data.sourceUrl ?
|
||||||
|
<a class="text-blue-500 hover:underline w-fit" href={e.data.sourceUrl}>Quelle</a>
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from "astro:content";
|
import {getCollection} from "astro:content";
|
||||||
import NavbarLayout from "../../layouts/NavbarLayout.astro";
|
import NavbarLayout from "../../layouts/NavbarLayout.astro";
|
||||||
import {astroI18n, createGetStaticPaths} from "astro-i18n";
|
import {astroI18n, createGetStaticPaths} from "astro-i18n";
|
||||||
|
|
||||||
@ -7,24 +7,24 @@ export const getStaticPaths = createGetStaticPaths(async () => {
|
|||||||
let posts = await getCollection("help");
|
let posts = await getCollection("help");
|
||||||
|
|
||||||
return posts.filter(value => value.id.split("/")[0] === astroI18n.locale).map((page) => ({
|
return posts.filter(value => value.id.split("/")[0] === astroI18n.locale).map((page) => ({
|
||||||
props: { page }, params: { slug: page.slug }
|
props: {page}, params: {slug: page.slug},
|
||||||
}) );
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
const { page } = Astro.props;
|
const {page} = Astro.props;
|
||||||
const { Content } = await page.render();
|
const {Content} = await page.render();
|
||||||
---
|
---
|
||||||
|
|
||||||
<NavbarLayout title={page.data.title}>
|
<NavbarLayout title={page.data.title}>
|
||||||
<article>
|
<article>
|
||||||
<h1 class="text-left">{page.data.title}</h1>
|
<h1 class="text-left">{page.data.title}</h1>
|
||||||
<Content />
|
<Content/>
|
||||||
</article>
|
</article>
|
||||||
</NavbarLayout>
|
</NavbarLayout>
|
||||||
|
|
||||||
<style is:global>
|
<style is:global>
|
||||||
article {
|
article {
|
||||||
>* {
|
> * {
|
||||||
all: revert;
|
all: revert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import NavbarLayout from "@layouts/NavbarLayout.astro";
|
import NavbarLayout from "@layouts/NavbarLayout.astro";
|
||||||
|
|
||||||
import { Image } from "astro:assets";
|
import {Image} from "astro:assets";
|
||||||
import localBau from "@images/2023-10-08_20.43.43.png";
|
import localBau from "@images/2023-10-08_20.43.43.png";
|
||||||
import {CaretRight, Archive, Rocket, Bell} from "@astropub/icons";
|
import {CaretRight, Archive, Rocket, Bell} from "@astropub/icons";
|
||||||
import {t} from "astro-i18n";
|
import {t} from "astro-i18n";
|
||||||
@ -35,22 +35,30 @@ function mapMap<T, K, J>(i: Map<T, K>, fn: (key: T, value: K) => J): J[] {
|
|||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<NavbarLayout title={t("home.page")} description="SteamWar.de Homepage">
|
<NavbarLayout title={t("home.page")} description="SteamWar.de Homepage">
|
||||||
<div class="w-full h-screen relative mb-4">
|
<div class="w-full h-screen relative mb-4">
|
||||||
<Image src={localBau} alt="Bau" width="1920" height="1080" densities={[1.5, 2, 3, 4]} class="w-full object-cover rounded-b-2xl shadow-2xl dark:brightness-75" style="height: calc(100vh + 1rem)" draggable="false" />
|
<Image src={localBau} alt="Bau" width="1920" height="1080" densities={[1.5, 2, 3, 4]}
|
||||||
|
class="w-full object-cover rounded-b-2xl shadow-2xl dark:brightness-75"
|
||||||
|
style="height: calc(100vh + 1rem)" draggable="false"/>
|
||||||
<drop-in class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 flex flex-col items-center">
|
<drop-in class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 flex flex-col items-center">
|
||||||
<h1 class="text-4xl sm:text-6xl md:text-8xl font-extrabold text-white -translate-y-16 opacity-0 barlow" style="transition: transform .7s ease-out, opacity .7s linear; text-shadow: 2px 2px 5px black;">
|
<h1 class="text-4xl sm:text-6xl md:text-8xl font-extrabold text-white -translate-y-16 opacity-0 barlow"
|
||||||
<span class="text-yellow-400">{t("home.title.first")}</span><span class="text-neutral-600">{t("home.title.second")}</span>
|
style="transition: transform .7s ease-out, opacity .7s linear; text-shadow: 2px 2px 5px black;">
|
||||||
|
<span class="text-yellow-400">{t("home.title.first")}</span><span
|
||||||
|
class="text-neutral-600">{t("home.title.second")}</span>
|
||||||
</h1>
|
</h1>
|
||||||
<text-carousel class="h-20 w-full relative select-none">
|
<text-carousel class="h-20 w-full relative select-none">
|
||||||
<h2 class="-translate-y-16">{t("home.subtitle.1")}</h2>
|
<h2 class="-translate-y-16">{t("home.subtitle.1")}</h2>
|
||||||
<h2>{t("home.subtitle.2")}<PlayerCount client:only="svelte" /></h2>
|
<h2>{t("home.subtitle.2")}
|
||||||
|
<PlayerCount client:only="svelte"/>
|
||||||
|
</h2>
|
||||||
<h2>{t("home.subtitle.3")}</h2>
|
<h2>{t("home.subtitle.3")}</h2>
|
||||||
</text-carousel>
|
</text-carousel>
|
||||||
<a href={l("join")} class="btn mt-32 px-8 flex opacity-0 -translate-y-16" style="transition: transform .3s ease-out, opacity .3s linear">{t("home.join")} <CaretRight width="24" heigth="24" /></a>
|
<a href={l("join")} class="btn mt-32 px-8 flex opacity-0 -translate-y-16"
|
||||||
|
style="transition: transform .3s ease-out, opacity .3s linear">{t("home.join")}
|
||||||
|
<CaretRight width="24" heigth="24"/>
|
||||||
|
</a>
|
||||||
<script>
|
<script>
|
||||||
class TextCarousel extends HTMLElement {
|
class TextCarousel extends HTMLElement {
|
||||||
current = 0;
|
current = 0;
|
||||||
@ -87,7 +95,7 @@ function mapMap<T, K, J>(i: Map<T, K>, fn: (key: T, value: K) => J): J[] {
|
|||||||
class DropIn extends HTMLElement {
|
class DropIn extends HTMLElement {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
for (let child of this.children) {
|
for (let child of this.children) {
|
||||||
if(child.classList.contains("opacity-0")) {
|
if (child.classList.contains("opacity-0")) {
|
||||||
child.classList.remove("opacity-0");
|
child.classList.remove("opacity-0");
|
||||||
child.classList.remove("-translate-y-16");
|
child.classList.remove("-translate-y-16");
|
||||||
} else {
|
} else {
|
||||||
@ -106,24 +114,26 @@ function mapMap<T, K, J>(i: Map<T, K>, fn: (key: T, value: K) => J): J[] {
|
|||||||
<section class="w-full flex flex-col items-center justify-center shadow-2xl rounded-b-2xl pb-8">
|
<section class="w-full flex flex-col items-center justify-center shadow-2xl rounded-b-2xl pb-8">
|
||||||
<div class="py-10 flex flex-col lg:flex-row">
|
<div class="py-10 flex flex-col lg:flex-row">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<Archive heigth="64" width="64" />
|
<Archive heigth="64" width="64"/>
|
||||||
<h1>{t("home.benefits.historic.title")}</h1>
|
<h1>{t("home.benefits.historic.title")}</h1>
|
||||||
<p>{t("home.benefits.historic.description.1")}</p>
|
<p>{t("home.benefits.historic.description.1")}</p>
|
||||||
<p>{t("home.benefits.historic.description.2")}</p>
|
<p>{t("home.benefits.historic.description.2")}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<Rocket heigth="64" width="64" />
|
<Rocket heigth="64" width="64"/>
|
||||||
<h1>{t("home.benefits.server.title")}</h1>
|
<h1>{t("home.benefits.server.title")}</h1>
|
||||||
<p>{t("home.benefits.server.description")}</p>
|
<p>{t("home.benefits.server.description")}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<Bell heigth="64" width="64" />
|
<Bell heigth="64" width="64"/>
|
||||||
<h1>{t("home.benefits.events.title")}</h1>
|
<h1>{t("home.benefits.events.title")}</h1>
|
||||||
<p>{t("home.benefits.events.description.1")}</p>
|
<p>{t("home.benefits.events.description.1")}</p>
|
||||||
<p>{t("home.benefits.events.description.2")}</p>
|
<p>{t("home.benefits.events.description.2")}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn px-8 flex" href={l("/about")}>{t("home.benefits.read")}<CaretRight width="24" heigth="24" /></a>
|
<a class="btn px-8 flex" href={l("/about")}>{t("home.benefits.read")}
|
||||||
|
<CaretRight width="24" heigth="24"/>
|
||||||
|
</a>
|
||||||
</section>
|
</section>
|
||||||
<section class="w-full py-12">
|
<section class="w-full py-12">
|
||||||
{mapMap(groupedTeamMember, (key, value) => (
|
{mapMap(groupedTeamMember, (key, value) => (
|
||||||
@ -136,7 +146,9 @@ function mapMap<T, K, J>(i: Map<T, K>, fn: (key: T, value: K) => J): J[] {
|
|||||||
dark:bg-neutral-900 dark:border-gray-800 dark:text-white">
|
dark:bg-neutral-900 dark:border-gray-800 dark:text-white">
|
||||||
<figure class="flex flex-col items-center">
|
<figure class="flex flex-col items-center">
|
||||||
<figcaption class="text-center mb-4 text-2xl">{v.name}</figcaption>
|
<figcaption class="text-center mb-4 text-2xl">{v.name}</figcaption>
|
||||||
<Image src={`https://visage.surgeplay.com/bust/150/${v.uuid}`} class="transition duration-300 ease-in-out hover:scale-110 hover:drop-shadow-2xl" alt={v.name + "s bust"} width="150" height="150" />
|
<Image src={`https://visage.surgeplay.com/bust/150/${v.uuid}`}
|
||||||
|
class="transition duration-300 ease-in-out hover:scale-110 hover:drop-shadow-2xl"
|
||||||
|
alt={v.name + "s bust"} width="150" height="150"/>
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -147,8 +159,8 @@ function mapMap<T, K, J>(i: Map<T, K>, fn: (key: T, value: K) => J): J[] {
|
|||||||
</NavbarLayout>
|
</NavbarLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
text-carousel{
|
text-carousel {
|
||||||
>* {
|
> * {
|
||||||
@apply absolute top-0 left-0 w-full text-xl sm:text-4xl italic text-white text-center opacity-0;
|
@apply absolute top-0 left-0 w-full text-xl sm:text-4xl italic text-white text-center opacity-0;
|
||||||
transition: transform .5s ease-out, opacity .5s linear;
|
transition: transform .5s ease-out, opacity .5s linear;
|
||||||
text-shadow: 2px 2px 5px black;
|
text-shadow: 2px 2px 5px black;
|
||||||
@ -163,13 +175,16 @@ function mapMap<T, K, J>(i: Map<T, K>, fn: (key: T, value: K) => J): J[] {
|
|||||||
@apply w-72 border-2 bg-zinc-50 border-gray-100 flex flex-col items-center p-8 m-4 rounded-xl shadow-lg transition-transform duration-300 ease-in-out
|
@apply w-72 border-2 bg-zinc-50 border-gray-100 flex flex-col items-center p-8 m-4 rounded-xl shadow-lg transition-transform duration-300 ease-in-out
|
||||||
dark:bg-zinc-900 dark:border-gray-800 dark:text-gray-100
|
dark:bg-zinc-900 dark:border-gray-800 dark:text-gray-100
|
||||||
hover:scale-105;
|
hover:scale-105;
|
||||||
>h1 {
|
|
||||||
|
> h1 {
|
||||||
@apply text-xl font-bold mt-4;
|
@apply text-xl font-bold mt-4;
|
||||||
}
|
}
|
||||||
>p {
|
|
||||||
|
> p {
|
||||||
@apply mt-4;
|
@apply mt-4;
|
||||||
}
|
}
|
||||||
>svg {
|
|
||||||
|
> svg {
|
||||||
@apply transition-transform duration-300 ease-in-out hover:scale-110 hover:drop-shadow-2xl
|
@apply transition-transform duration-300 ease-in-out hover:scale-110 hover:drop-shadow-2xl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,17 @@ 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";
|
||||||
|
|
||||||
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");
|
window.location.href = l("/dashboard");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</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" />
|
<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 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);">
|
dark:text-white " style="width: min(100vw, 75em);">
|
||||||
<Login client:load />
|
<Login client:load/>
|
||||||
</div>
|
</div>
|
||||||
</NavbarLayout>
|
</NavbarLayout>
|
@ -9,22 +9,22 @@ export const getStaticPaths = createGetStaticPaths(async () => {
|
|||||||
|
|
||||||
return modes.map(value => ({
|
return modes.map(value => ({
|
||||||
props: {
|
props: {
|
||||||
mode: value
|
mode: value,
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
gamemode: value.id
|
gamemode: value.id,
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
mode: CollectionEntry<"modes">
|
mode: CollectionEntry<"modes">;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { mode } = Astro.props;
|
const {mode} = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout title={t("elo.title", {mode: t(`${mode.data.translationKey}.title`)})}>
|
<PageLayout title={t("elo.title", {mode: t(`${mode.data.translationKey}.title`)})}>
|
||||||
<h1 class="text-2xl mb-2">{t("ranking.heading", { mode: t(`${mode.data.translationKey}.title`) })}</h1>
|
<h1 class="text-2xl mb-2">{t("ranking.heading", {mode: t(`${mode.data.translationKey}.title`)})}</h1>
|
||||||
<EloTable gamemode={mode.id} client:only="svelte" />
|
<EloTable gamemode={mode.id} client:only="svelte"/>
|
||||||
</PageLayout>
|
</PageLayout>
|
@ -18,7 +18,7 @@ export const getStaticPaths = createGetStaticPaths(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return posts.map((page) => ({
|
return posts.map((page) => ({
|
||||||
props: { page, german: page.id.split("/")[0] != astroI18n.locale }, params: { mode: page.slug.split("/")[1] }
|
props: {page, german: page.id.split("/")[0] != astroI18n.locale}, params: {mode: page.slug.split("/")[1]},
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -27,16 +27,16 @@ interface Props {
|
|||||||
german: boolean
|
german: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const { page, german } = Astro.props;
|
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("title", {mode: t(`${page.data.translationKey}.title`)}, {route: "/rules"})}>
|
||||||
<article>
|
<article>
|
||||||
{german && (
|
{german && (
|
||||||
<LanguageWarning />
|
<LanguageWarning/>
|
||||||
)}
|
)}
|
||||||
<Content />
|
<Content/>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -57,7 +57,7 @@ const { Content } = await page.render();
|
|||||||
|
|
||||||
<style is:global>
|
<style is:global>
|
||||||
article {
|
article {
|
||||||
>* {
|
> * {
|
||||||
all: revert;
|
all: revert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ const imageMap = {
|
|||||||
"mwg": mwg,
|
"mwg": mwg,
|
||||||
"as": as,
|
"as": as,
|
||||||
"ws": ws,
|
"ws": ws,
|
||||||
"qg": mwg
|
"qg": mwg,
|
||||||
};
|
};
|
||||||
|
|
||||||
const modes = await getCollection("modes", entry => entry.data.main);
|
const modes = await getCollection("modes", entry => entry.data.main);
|
||||||
@ -24,14 +24,19 @@ const modes = await getCollection("modes", entry => entry.data.main);
|
|||||||
{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]} alt={t(value.data.translationKey + ".title")} class="dark:invert"></Image>
|
<Image height="300" width="300" src={imageMap[value.data.translationKey]}
|
||||||
|
alt={t(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(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")}</a>
|
||||||
<a href={l(`/announcements/tags/${value.id}`)} class="text-yellow-300 hover:underline w-fit">{t("announcements")}</a>
|
<a href={l(`/announcements/tags/${value.id}`)}
|
||||||
{value.data.ranked ? <a href={l(`/ranked/${value.id}`)} class="text-yellow-300 hover:underline w-fit">{t("ranking")}</a> : null}
|
class="text-yellow-300 hover:underline w-fit">{t("announcements")}</a>
|
||||||
|
{value.data.ranked
|
||||||
|
? <a href={l(`/ranked/${value.id}`)}
|
||||||
|
class="text-yellow-300 hover:underline w-fit">{t("ranking")}</a>
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>))}
|
</div>))}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
import PageLayout from "../../layouts/PageLayout.astro";
|
import PageLayout from "../../layouts/PageLayout.astro";
|
||||||
import FightStatistics from "../../components/FightStatistics.svelte";
|
import FightStatistics from "../../components/FightStatistics.svelte";
|
||||||
import {t} from "astro-i18n";
|
import {t} from "astro-i18n";
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout title={t("stats.title")}>
|
<PageLayout title={t("stats.title")}>
|
||||||
<FightStatistics client:only="svelte" />
|
<FightStatistics client:only="svelte"/>
|
||||||
</PageLayout>
|
</PageLayout>
|
@ -20,7 +20,7 @@
|
|||||||
.btn {
|
.btn {
|
||||||
@apply bg-yellow-400 font-bold py-2 px-4 rounded cursor-pointer select-none mx-2 text-black flex flex-row;
|
@apply bg-yellow-400 font-bold py-2 px-4 rounded cursor-pointer select-none mx-2 text-black flex flex-row;
|
||||||
@apply hover:bg-yellow-300 hover:text-black hover:shadow-2xl hover:scale-105;
|
@apply hover:bg-yellow-300 hover:text-black hover:shadow-2xl hover:scale-105;
|
||||||
transition: all 0.5s cubic-bezier(.2,3,.67,.6),
|
transition: all 0.5s cubic-bezier(.2, 3, .67, .6),
|
||||||
background-color .1s ease-in-out,
|
background-color .1s ease-in-out,
|
||||||
outline-width .1s ease-in-out,
|
outline-width .1s ease-in-out,
|
||||||
outline-color .1s ease-in-out;
|
outline-color .1s ease-in-out;
|
||||||
@ -34,16 +34,16 @@
|
|||||||
.btn-dropdown {
|
.btn-dropdown {
|
||||||
@apply relative mx-2;
|
@apply relative mx-2;
|
||||||
|
|
||||||
>:nth-child(1) {
|
> :nth-child(1) {
|
||||||
@apply !mx-0;
|
@apply !mx-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
>:nth-child(2) {
|
> :nth-child(2) {
|
||||||
@apply hidden absolute top-full left-1/2 -translate-x-1/2 bg-gray-800 list-none text-white rounded py-2 flex-col text-sm z-20;
|
@apply hidden absolute top-full left-1/2 -translate-x-1/2 bg-gray-800 list-none text-white rounded py-2 flex-col text-sm z-20;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover,&:focus-within {
|
&:hover, &:focus-within {
|
||||||
>:nth-child(2) {
|
> :nth-child(2) {
|
||||||
@apply flex;
|
@apply flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,14 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { l as proxyL } from "astro-i18n";
|
import {l as proxyL} from "astro-i18n";
|
||||||
|
|
||||||
const locales = ["de"];
|
const locales = ["de"];
|
||||||
|
|
||||||
export const l = (route: string) => {
|
export const l = (route: string) => {
|
||||||
const transPath = proxyL(route);
|
const transPath = proxyL(route);
|
||||||
|
|
||||||
if(import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
return transPath;
|
return transPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren