New Dashboard
This commit is contained in:
@ -7,7 +7,6 @@ import sitemap from "@astrojs/sitemap";
|
|||||||
import robotsTxt from "astro-robots-txt";
|
import robotsTxt from "astro-robots-txt";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import mdx from "@astrojs/mdx";
|
import mdx from "@astrojs/mdx";
|
||||||
import pagefind from "astro-pagefind";
|
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@ -20,9 +19,8 @@ export default defineConfig({
|
|||||||
integrations: [
|
integrations: [
|
||||||
svelte(),
|
svelte(),
|
||||||
tailwind({
|
tailwind({
|
||||||
configFile: "./tailwind.config.cjs",
|
configFile: "./tailwind.config.js",
|
||||||
}),
|
}),
|
||||||
pagefind(),
|
|
||||||
configureI18n(),
|
configureI18n(),
|
||||||
sitemap({
|
sitemap({
|
||||||
i18n: {
|
i18n: {
|
||||||
@ -68,6 +66,7 @@ export default defineConfig({
|
|||||||
"@layouts": path.resolve("./src/layouts"),
|
"@layouts": path.resolve("./src/layouts"),
|
||||||
"@repo": path.resolve("./src/components/repo"),
|
"@repo": path.resolve("./src/components/repo"),
|
||||||
"@stores": path.resolve("./src/components/stores"),
|
"@stores": path.resolve("./src/components/stores"),
|
||||||
|
"$lib": path.resolve("./src"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
20
package.json
20
package.json
@ -20,25 +20,38 @@
|
|||||||
"@astrojs/svelte": "^7.0.4",
|
"@astrojs/svelte": "^7.0.4",
|
||||||
"@astrojs/tailwind": "^5.1.5",
|
"@astrojs/tailwind": "^5.1.5",
|
||||||
"@astropub/icons": "^0.2.0",
|
"@astropub/icons": "^0.2.0",
|
||||||
|
"@internationalized/date": "^3.7.0",
|
||||||
"@types/color": "^4.2.0",
|
"@types/color": "^4.2.0",
|
||||||
"@types/node": "^22.9.3",
|
"@types/node": "^22.9.3",
|
||||||
"@types/three": "^0.170.0",
|
"@types/three": "^0.170.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
||||||
"@typescript-eslint/parser": "^8.15.0",
|
"@typescript-eslint/parser": "^8.15.0",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
|
"bits-ui": "1.3.4",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"cmdk-sv": "^0.0.18",
|
||||||
"cssnano": "^7.0.6",
|
"cssnano": "^7.0.6",
|
||||||
|
"embla-carousel-svelte": "^8.5.2",
|
||||||
"esbuild": "^0.24.0",
|
"esbuild": "^0.24.0",
|
||||||
"eslint": "^9.15.0",
|
"eslint": "^9.15.0",
|
||||||
"eslint-plugin-astro": "^1.3.1",
|
"eslint-plugin-astro": "^1.3.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"eslint-plugin-svelte": "^2.46.0",
|
"eslint-plugin-svelte": "^2.46.0",
|
||||||
|
"formsnap": "1.0.1",
|
||||||
|
"lucide-svelte": "^0.476.0",
|
||||||
|
"mode-watcher": "^0.5.1",
|
||||||
|
"paneforge": "^0.0.6",
|
||||||
"postcss-nesting": "^13.0.1",
|
"postcss-nesting": "^13.0.1",
|
||||||
"sass": "^1.81.0",
|
"sass": "^1.81.0",
|
||||||
"svelte": "^5.16.0",
|
"svelte": "^5.16.0",
|
||||||
|
"svelte-sonner": "^0.3.28",
|
||||||
"tailwind-merge": "^2.5.5",
|
"tailwind-merge": "^2.5.5",
|
||||||
|
"tailwind-variants": "^0.3.1",
|
||||||
"tailwindcss": "^3.4.15",
|
"tailwindcss": "^3.4.15",
|
||||||
"three": "^0.170.0",
|
"three": "^0.170.0",
|
||||||
"typescript": "^5.7.2"
|
"typescript": "^5.7.2",
|
||||||
|
"vaul-svelte": "^0.3.2",
|
||||||
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/mdx": "^4.0.7",
|
"@astrojs/mdx": "^4.0.7",
|
||||||
@ -46,9 +59,9 @@
|
|||||||
"@codemirror/commands": "^6.8.0",
|
"@codemirror/commands": "^6.8.0",
|
||||||
"@codemirror/lang-json": "^6.0.1",
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
"@ddietr/codemirror-themes": "^1.4.4",
|
"@ddietr/codemirror-themes": "^1.4.4",
|
||||||
|
"@tanstack/table-core": "^8.21.2",
|
||||||
"astro": "^5.1.8",
|
"astro": "^5.1.8",
|
||||||
"astro-i18n": "^2.2.4",
|
"astro-i18n": "^2.2.4",
|
||||||
"astro-pagefind": "^1.6.0",
|
|
||||||
"astro-robots-txt": "^1.0.0",
|
"astro-robots-txt": "^1.0.0",
|
||||||
"astro-seo": "^0.8.4",
|
"astro-seo": "^0.8.4",
|
||||||
"chart.js": "^4.4.6",
|
"chart.js": "^4.4.6",
|
||||||
@ -64,7 +77,6 @@
|
|||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
"svelte-awesome": "^3.3.5",
|
"svelte-awesome": "^3.3.5",
|
||||||
"svelte-codemirror-editor": "^1.4.1",
|
"svelte-codemirror-editor": "^1.4.1",
|
||||||
"svelte-spa-router": "^4.0.1",
|
"svelte-spa-router": "^4.0.1"
|
||||||
"zod": "^3.23.8"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1022
pnpm-lock.yaml
generated
1022
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {window} from "./util.ts";
|
import {window} from "./utils.ts";
|
||||||
import {astroI18n, t} from "astro-i18n";
|
import {astroI18n, t} from "astro-i18n";
|
||||||
import type {EventFight, ExtendedEvent} from "@type/event";
|
import type {EventFight, ExtendedEvent} from "@type/event";
|
||||||
import "@styles/table.css";
|
import "@styles/table.css";
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {window} from "./util.ts";
|
import {window} from "./utils.ts";
|
||||||
import {t} from "astro-i18n";
|
import {t} from "astro-i18n";
|
||||||
import type {ExtendedEvent} from "@type/event.ts";
|
import type {ExtendedEvent} from "@type/event.ts";
|
||||||
import "@styles/table.css"
|
import "@styles/table.css"
|
||||||
|
|||||||
@ -38,6 +38,7 @@
|
|||||||
</NavBrand>
|
</NavBrand>
|
||||||
<NavHamburger onclick={toggle}/>
|
<NavHamburger onclick={toggle}/>
|
||||||
<NavUl {hidden}>
|
<NavUl {hidden}>
|
||||||
|
<NavLi href="/admin/new">New UI</NavLi>
|
||||||
<NavLi href="#/edit">Edit Pages</NavLi>
|
<NavLi href="#/edit">Edit Pages</NavLi>
|
||||||
<NavLi href="#/perms">Permissions</NavLi>
|
<NavLi href="#/perms">Permissions</NavLi>
|
||||||
</NavUl>
|
</NavUl>
|
||||||
|
|||||||
@ -34,7 +34,7 @@
|
|||||||
dirty?: boolean;
|
dirty?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { pageId, branch, dirty = $bindable(false) }: Props = $props();
|
let { pageId, branch = $bindable(), dirty = $bindable(false) }: Props = $props();
|
||||||
|
|
||||||
let dispatcher = createEventDispatcher();
|
let dispatcher = createEventDispatcher();
|
||||||
|
|
||||||
@ -97,7 +97,7 @@
|
|||||||
{#if page?.name.endsWith("md") || page?.name.endsWith("mdx")}
|
{#if page?.name.endsWith("md") || page?.name.endsWith("mdx")}
|
||||||
<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} onchange={() => dirty = true}/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:catch error}
|
{:catch error}
|
||||||
|
|||||||
56
src/components/moderator/App.svelte
Normal file
56
src/components/moderator/App.svelte
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import type {RouteDefinition} from "svelte-spa-router";
|
||||||
|
import Router from "svelte-spa-router";
|
||||||
|
import NavLinks from "@components/moderator/layout/NavLinks.svelte";
|
||||||
|
import {Switch} from "@components/ui/switch";
|
||||||
|
import {Label} from "@components/ui/label";
|
||||||
|
import {navigate} from "astro:transitions/client";
|
||||||
|
import Players from "@components/moderator/pages/players/Players.svelte";
|
||||||
|
import Events from "@components/moderator/pages/events/Events.svelte";
|
||||||
|
import Dashboard from "@components/moderator/pages/dashboard/Dashboard.svelte";
|
||||||
|
import Event from "@components/moderator/pages/event/Event.svelte";
|
||||||
|
|
||||||
|
const routes: RouteDefinition = {
|
||||||
|
"/": Dashboard,
|
||||||
|
"/events": Events,
|
||||||
|
"/players": Players,
|
||||||
|
"/event/:id": Event
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col bg-background min-w-full min-h-screen">
|
||||||
|
<div class="border-b">
|
||||||
|
<div class="flex h-16 items-center px-4">
|
||||||
|
<a href="/" class="text-sm font-bold transition-colors text-primary">
|
||||||
|
SteamWar
|
||||||
|
</a>
|
||||||
|
<NavLinks />
|
||||||
|
<div class="ml-auto flex items-center space-x-4">
|
||||||
|
<Switch id="new-ui-switch" checked={true} on:click={() => navigate("/admin")} />
|
||||||
|
<Label for="new-ui-switch">New UI!</Label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<main class="flex flex-col">
|
||||||
|
<Router {routes} />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
42
src/components/moderator/components/EventCard.svelte
Normal file
42
src/components/moderator/components/EventCard.svelte
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import type {ShortEvent} from "@type/event.ts";
|
||||||
|
import {Card, CardContent, CardHeader, CardTitle} from "@components/ui/card";
|
||||||
|
|
||||||
|
let { event }: { event: ShortEvent } = $props();
|
||||||
|
|
||||||
|
let sameDate = $derived(new Intl.DateTimeFormat().format(event.start) === new Intl.DateTimeFormat().format(event.end));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{event.name}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
{#if !sameDate}
|
||||||
|
<p>Startet: {new Intl.DateTimeFormat().format(event.start)}</p>
|
||||||
|
<p>Endet: {new Intl.DateTimeFormat().format(event.end)}</p>
|
||||||
|
{:else}
|
||||||
|
<p>Am: {new Intl.DateTimeFormat().format(event.start)}</p>
|
||||||
|
<p> </p>
|
||||||
|
{/if}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
40
src/components/moderator/layout/NavLinks.svelte
Normal file
40
src/components/moderator/layout/NavLinks.svelte
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {location} from "svelte-spa-router";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav class="flex items-center space-x-4 lg:space-x-6 mx-6">
|
||||||
|
<a href="#/" class="hover:text-primary text-sm font-medium transition-colors" class:text-muted={$location !== "/"}>
|
||||||
|
Dashboard
|
||||||
|
</a>
|
||||||
|
<a href="#/events" class="hover:text-primary text-sm font-medium transition-colors" class:text-muted={$location !== "/events"}>
|
||||||
|
Events
|
||||||
|
</a>
|
||||||
|
<a href="#/players" class="hover:text-primary text-sm font-medium transition-colors" class:text-muted={$location !== "/players"}>
|
||||||
|
Players
|
||||||
|
</a>
|
||||||
|
<a href="#/pages" class="hover:text-primary text-sm font-medium transition-colors" class:text-muted={$location !== "/pages"}>
|
||||||
|
Pages
|
||||||
|
</a>
|
||||||
|
<a href="#/schematics" class="hover:text-primary text-sm font-medium transition-colors" class:text-muted={$location !== "/schematics"}>
|
||||||
|
Schematics
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
22
src/components/moderator/pages/dashboard/Dashboard.svelte
Normal file
22
src/components/moderator/pages/dashboard/Dashboard.svelte
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div class="p-4">
|
||||||
|
<h1 class="font-bold text-xl">SteamWar Dashboard</h1>
|
||||||
|
</div>
|
||||||
38
src/components/moderator/pages/event/Event.svelte
Normal file
38
src/components/moderator/pages/event/Event.svelte
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {eventRepo} from "@repo/event.ts";
|
||||||
|
import EventView from "@components/moderator/pages/event/EventView.svelte";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
params: { id: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
let { params }: Props = $props();
|
||||||
|
|
||||||
|
let id = params.id;
|
||||||
|
let event = $eventRepo.getEvent(id.toString());
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await event}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{:then data}
|
||||||
|
<EventView event={data} />
|
||||||
|
{/await}
|
||||||
128
src/components/moderator/pages/event/EventEdit.svelte
Normal file
128
src/components/moderator/pages/event/EventEdit.svelte
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {Input} from "@components/ui/input";
|
||||||
|
import {Label} from "@components/ui/label";
|
||||||
|
import {Popover, PopoverContent, PopoverTrigger} from "@components/ui/popover";
|
||||||
|
import type {SWEvent} from "@type/event.ts"
|
||||||
|
import DateTimePicker from "@components/ui/datetime-picker/DateTimePicker.svelte";
|
||||||
|
import {fromAbsolute} from "@internationalized/date";
|
||||||
|
import {Button} from "@components/ui/button";
|
||||||
|
import {ChevronsUpDown} from "lucide-svelte";
|
||||||
|
import {Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList} from "@components/ui/command";
|
||||||
|
import {schemTypes} from "@stores/stores.ts";
|
||||||
|
import Check from "lucide-svelte/icons/check";
|
||||||
|
import {cn} from "@components/utils.ts";
|
||||||
|
import {Switch} from "@components/ui/switch";
|
||||||
|
import {eventRepo} from "@repo/event.ts";
|
||||||
|
|
||||||
|
const { event }: { event: SWEvent } = $props();
|
||||||
|
|
||||||
|
let rootEvent: SWEvent = $state(event)
|
||||||
|
|
||||||
|
let eventName = $state(rootEvent.name);
|
||||||
|
let eventDeadline = $state(fromAbsolute(rootEvent.deadline, "Europe/Berlin"));
|
||||||
|
let eventStart = $state(fromAbsolute(rootEvent.start, "Europe/Berlin"));
|
||||||
|
let eventEnd = $state(fromAbsolute(rootEvent.end, "Europe/Berlin"));
|
||||||
|
let eventTeamSize = $state(rootEvent.maxTeamMembers);
|
||||||
|
let eventSchematicType = $state(rootEvent.schemType);
|
||||||
|
let eventPublicsOnly = $state(rootEvent.publicSchemsOnly);
|
||||||
|
|
||||||
|
let dirty = $derived(eventName !== rootEvent.name ||
|
||||||
|
eventDeadline.toDate().getTime() !== rootEvent.deadline ||
|
||||||
|
eventStart.toDate().getTime() !== rootEvent.start ||
|
||||||
|
eventEnd.toDate().getTime() !== rootEvent.end ||
|
||||||
|
eventTeamSize !== rootEvent.maxTeamMembers ||
|
||||||
|
eventSchematicType !== rootEvent.schemType ||
|
||||||
|
eventPublicsOnly !== rootEvent.publicSchemsOnly);
|
||||||
|
|
||||||
|
async function updateEvent() {
|
||||||
|
rootEvent = await $eventRepo.updateEvent(event.id.toString(), {
|
||||||
|
name: eventName,
|
||||||
|
deadline: eventDeadline.toDate().getTime(),
|
||||||
|
start: eventStart.toDate().getTime(),
|
||||||
|
end: eventEnd.toDate().getTime(),
|
||||||
|
maxTeamMembers: eventTeamSize,
|
||||||
|
schemType: eventSchematicType,
|
||||||
|
publicSchemsOnly: eventPublicsOnly,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<Label for="event-name">Name</Label>
|
||||||
|
<Input id="event-name" bind:value={eventName} />
|
||||||
|
<Label>Deadline</Label>
|
||||||
|
<DateTimePicker bind:value={eventDeadline} />
|
||||||
|
<Label>Start</Label>
|
||||||
|
<DateTimePicker bind:value={eventStart} />
|
||||||
|
<Label>End</Label>
|
||||||
|
<DateTimePicker bind:value={eventEnd} />
|
||||||
|
<Label for="event-size">Teamsize</Label>
|
||||||
|
<Input id="event-size" bind:value={eventTeamSize} type="number" />
|
||||||
|
<Label>Schematic Type</Label>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger>
|
||||||
|
{#snippet child({ props })}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
class="justify-between"
|
||||||
|
{...props}
|
||||||
|
role="combobox"
|
||||||
|
>
|
||||||
|
{$schemTypes.find(value => value.db === eventSchematicType)?.name || eventSchematicType || "Select a schematic type..."}
|
||||||
|
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
{/snippet}
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Search schematic types..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No schematic type found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{#each $schemTypes as type}
|
||||||
|
<CommandItem
|
||||||
|
value={type.db}
|
||||||
|
onSelect={() => {
|
||||||
|
eventSchematicType = type.db;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Check
|
||||||
|
class={cn(
|
||||||
|
"mr-2 size-4",
|
||||||
|
eventSchematicType !== type.db && "text-transparent"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{type.name}
|
||||||
|
</CommandItem>
|
||||||
|
{/each}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<Label for="event-publics">Publics Schematics Only</Label>
|
||||||
|
<Switch id="event-publics" bind:checked={eventPublicsOnly} />
|
||||||
|
<div class="flex flex-row justify-end border-t pt-2 gap-4">
|
||||||
|
<Button variant="destructive">Delete</Button>
|
||||||
|
<Button disabled={!dirty} onclick={updateEvent}>Update</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
107
src/components/moderator/pages/event/EventFightList.svelte
Normal file
107
src/components/moderator/pages/event/EventFightList.svelte
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import type {ExtendedEvent} from "@type/event";
|
||||||
|
import {createSvelteTable, FlexRender} from "@components/ui/data-table";
|
||||||
|
import {
|
||||||
|
type ColumnFiltersState,
|
||||||
|
getCoreRowModel, getFilteredRowModel,
|
||||||
|
getPaginationRowModel, getSortedRowModel,
|
||||||
|
type SortingState,
|
||||||
|
} from "@tanstack/table-core";
|
||||||
|
import { columns } from "./columns"
|
||||||
|
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@components/ui/table";
|
||||||
|
|
||||||
|
let { data }: { data: ExtendedEvent } = $props();
|
||||||
|
|
||||||
|
let sorting = $state<SortingState>([]);
|
||||||
|
let columnFilters = $state<ColumnFiltersState>([]);
|
||||||
|
|
||||||
|
const table = createSvelteTable({
|
||||||
|
get data() {
|
||||||
|
return data.fights;
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
get sorting() {
|
||||||
|
return sorting;
|
||||||
|
},
|
||||||
|
get columnFilters() {
|
||||||
|
return columnFilters;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onSortingChange: (updater) => {
|
||||||
|
if (typeof updater === "function") {
|
||||||
|
sorting = updater(sorting);
|
||||||
|
} else {
|
||||||
|
sorting = updater;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onColumnFiltersChange: (updater) => {
|
||||||
|
if (typeof updater === "function") {
|
||||||
|
columnFilters = updater(columnFilters);
|
||||||
|
} else {
|
||||||
|
columnFilters = updater;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
columns,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
||||||
|
<TableRow>
|
||||||
|
{#each headerGroup.headers as header (header.id)}
|
||||||
|
<TableHead>
|
||||||
|
{#if !header.isPlaceholder}
|
||||||
|
<FlexRender
|
||||||
|
content={header.column.columnDef.header}
|
||||||
|
context={header.getContext()}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</TableHead>
|
||||||
|
{/each}
|
||||||
|
</TableRow>
|
||||||
|
{/each}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{#each table.getRowModel().rows as row (row.id)}
|
||||||
|
<TableRow data-state={row.getIsSelected() && "selected"}>
|
||||||
|
{#each row.getVisibleCells() as cell (cell.id)}
|
||||||
|
<TableCell>
|
||||||
|
<FlexRender
|
||||||
|
content={cell.column.columnDef.cell}
|
||||||
|
context={cell.getContext()}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
{/each}
|
||||||
|
</TableRow>
|
||||||
|
{:else}
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colspan={columns.length} class="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
{/each}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
100
src/components/moderator/pages/event/EventView.svelte
Normal file
100
src/components/moderator/pages/event/EventView.svelte
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import type {ExtendedEvent} from "@type/event.ts";
|
||||||
|
import EventEdit from "@components/moderator/pages/event/EventEdit.svelte";
|
||||||
|
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@components/ui/table/index.js";
|
||||||
|
import {Button} from "@components/ui/button";
|
||||||
|
import {Popover, PopoverTrigger} from "@components/ui/popover";
|
||||||
|
import {PopoverContent} from "@components/ui/popover/index.js";
|
||||||
|
import {CommandEmpty, CommandGroup, CommandInput, CommandList, CommandItem, Command} from "@components/ui/command";
|
||||||
|
import {players} from "@stores/stores.ts";
|
||||||
|
import {eventRepo} from "@repo/event.ts";
|
||||||
|
import EventFightList from "@components/moderator/pages/event/EventFightList.svelte";
|
||||||
|
|
||||||
|
const {
|
||||||
|
event
|
||||||
|
}: { event: ExtendedEvent } = $props();
|
||||||
|
|
||||||
|
let referees = $state(event.event.referees)
|
||||||
|
|
||||||
|
async function addReferee(value: string) {
|
||||||
|
referees = (await $eventRepo.updateEvent(event.event.id.toString(), {
|
||||||
|
addReferee: [value]
|
||||||
|
})).referees;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeReferee(value: string) {
|
||||||
|
referees = (await $eventRepo.updateEvent(event.event.id.toString(), {
|
||||||
|
removeReferee: [value]
|
||||||
|
})).referees;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col m-4 p-4 rounded-md border gap-4">
|
||||||
|
<div class="flex flex-col md:flex-row">
|
||||||
|
<div class="w-1/2">
|
||||||
|
<h1 class="text-2xl font-bold mb-4">{event.event.name}</h1>
|
||||||
|
<EventEdit event={event.event} />
|
||||||
|
</div>
|
||||||
|
<div class="ml-4 pl-4 border-l w-1/2">
|
||||||
|
<h2>Referees</h2>
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Name</TableHead>
|
||||||
|
<TableHead>Actions</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{#each referees as referee (referee.uuid)}
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>{referee.name}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Button onclick={() => removeReferee(referee.uuid)}>Remove</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
{/each}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger>
|
||||||
|
<Button>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Search players..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No Players found :(</CommandEmpty>
|
||||||
|
<CommandGroup heading="Players">
|
||||||
|
{#each $players.filter(v => v.perms.length > 0).filter(v => !referees.some(k => k.uuid === v.uuid)) as player (player.uuid)}
|
||||||
|
<CommandItem value={player.uuid} onSelect={() => addReferee(player.uuid)}>{player.name}</CommandItem>
|
||||||
|
{/each}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<EventFightList data={event} />
|
||||||
|
</div>
|
||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is a part of the SteamWar software.
|
* This file is a part of the SteamWar software.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2023 SteamWar.de-Serverteam
|
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -17,17 +17,16 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function window<T>(arr: T[], len: number): T[][] {
|
import type {ColumnDef} from "@tanstack/table-core";
|
||||||
const result: T[][] = [];
|
import type {EventFight} from "@type/event.ts";
|
||||||
for (let i = 0; i < arr.length; i += len) {
|
|
||||||
result.push(arr.slice(i, i + len));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stopPropagation(a: any) {
|
export const columns: ColumnDef<EventFight> = [
|
||||||
return (e: Event) => {
|
{
|
||||||
e.stopPropagation();
|
accessorFn: (r) => r.blueTeam.name,
|
||||||
a(e);
|
header: "Team Blue",
|
||||||
};
|
},
|
||||||
}
|
{
|
||||||
|
accessorFn: (r) => r.redTeam.name,
|
||||||
|
header: "Team Red",
|
||||||
|
},
|
||||||
|
];
|
||||||
51
src/components/moderator/pages/events/Events.svelte
Normal file
51
src/components/moderator/pages/events/Events.svelte
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { eventRepo } from "@repo/event.ts";
|
||||||
|
import EventCard from "@components/moderator/components/EventCard.svelte";
|
||||||
|
|
||||||
|
let eventsFuture = $state($eventRepo.listEvents());
|
||||||
|
let millis = Date.now();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="p-4">
|
||||||
|
{#await eventsFuture}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{:then events}
|
||||||
|
<h1 class="mt-5 scroll-m-20 pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0">Upcoming</h1>
|
||||||
|
<div class="grid gap-4 p-4 border-b" style="grid-template-columns: repeat(auto-fill, minmax(300px, 1fr))">
|
||||||
|
{#each events.filter((e) => e.start > millis) as event (event.id)}
|
||||||
|
<a href="#/event/{event.id}">
|
||||||
|
<EventCard {event} />
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<h1 class="mt-5 scroll-m-20 pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0">Past</h1>
|
||||||
|
<div class="grid gap-4 p-4" style="grid-template-columns: repeat(auto-fill, minmax(300px, 1fr))">
|
||||||
|
{#each events.filter((e) => e.start < millis).reverse() as event (event.id)}
|
||||||
|
<a href="#/event/{event.id}">
|
||||||
|
<EventCard {event} />
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:catch e}
|
||||||
|
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {permissions, players} from "@stores/stores.ts";
|
||||||
|
import {Select, SelectContent, SelectItem} from "@components/ui/select";
|
||||||
|
import {SelectTrigger} from "@components/ui/select/index.js";
|
||||||
|
import {permsRepo} from "@repo/perms.ts";
|
||||||
|
|
||||||
|
const {
|
||||||
|
perms, uuid
|
||||||
|
}: { perms: string[], uuid: string } = $props();
|
||||||
|
|
||||||
|
let value = $state(perms);
|
||||||
|
let prevValue = $state(perms);
|
||||||
|
|
||||||
|
function onChange(change: string[]) {
|
||||||
|
$permissions.perms.forEach(perm => {
|
||||||
|
if (prevValue.includes(perm) && !change.includes(perm)) {
|
||||||
|
$permsRepo.removePerm(uuid, perm)
|
||||||
|
} else if (!prevValue.includes(perm) && change.includes(perm)) {
|
||||||
|
$permsRepo.addPerm(uuid, perm)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
prevValue = change;
|
||||||
|
value = change;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Select type="multiple" bind:value onValueChange={onChange}>
|
||||||
|
<SelectTrigger>
|
||||||
|
{value.length} Permissions
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{#each $permissions.perms as permission (permission)}
|
||||||
|
<SelectItem value={permission}>{permission}</SelectItem>
|
||||||
|
{/each}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
32
src/components/moderator/pages/players/Players.svelte
Normal file
32
src/components/moderator/pages/players/Players.svelte
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Table from "@components/moderator/pages/players/Table.svelte";
|
||||||
|
|
||||||
|
import {dataRepo} from "@repo/data";
|
||||||
|
|
||||||
|
let playersFuture = $state($dataRepo.getPlayers())
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await playersFuture}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{:then players}
|
||||||
|
<Table data={players} />
|
||||||
|
{/await}
|
||||||
47
src/components/moderator/pages/players/PrefixDropdown.svelte
Normal file
47
src/components/moderator/pages/players/PrefixDropdown.svelte
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {Select, SelectContent, SelectItem, SelectTrigger} from "@components/ui/select";
|
||||||
|
import {permissions} from "@stores/stores.ts";
|
||||||
|
import {permsRepo} from "@repo/perms.ts";
|
||||||
|
|
||||||
|
const {
|
||||||
|
prefix, uuid
|
||||||
|
}: { prefix: string, uuid: string } = $props();
|
||||||
|
|
||||||
|
let value = $state(prefix);
|
||||||
|
|
||||||
|
function onChange(change: string) {
|
||||||
|
$permsRepo.setPrefix(uuid, change);
|
||||||
|
|
||||||
|
value = $permissions.prefixes[change].chatPrefix;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Select type="single" bind:value onValueChange={onChange}>
|
||||||
|
<SelectTrigger>
|
||||||
|
{value === "" ? "None" : value}
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{#each Object.entries($permissions.prefixes) as prefix (prefix[1].name)}
|
||||||
|
<SelectItem value={prefix[0]}>{prefix[1].chatPrefix === "" ? "None" : prefix[1].chatPrefix}</SelectItem>
|
||||||
|
{/each}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
174
src/components/moderator/pages/players/Table.svelte
Normal file
174
src/components/moderator/pages/players/Table.svelte
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
type ColumnFiltersState,
|
||||||
|
getCoreRowModel, getFilteredRowModel,
|
||||||
|
getPaginationRowModel, getSortedRowModel,
|
||||||
|
type PaginationState,
|
||||||
|
type SortingState,
|
||||||
|
} from "@tanstack/table-core";
|
||||||
|
import {
|
||||||
|
createSvelteTable,
|
||||||
|
FlexRender,
|
||||||
|
} from "@components/ui/data-table/index";
|
||||||
|
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@components/ui/table";
|
||||||
|
import {Button} from "@components/ui/button";
|
||||||
|
import {Input} from "@components/ui/input";
|
||||||
|
import {Select} from "@components/ui/select";
|
||||||
|
import {SelectContent, SelectItem, SelectTrigger} from "@components/ui/select/index.js";
|
||||||
|
import type {Player} from "@type/data";
|
||||||
|
import { columns } from "./columns";
|
||||||
|
|
||||||
|
let { data }: { data: Player[] } = $props();
|
||||||
|
|
||||||
|
let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 25 });
|
||||||
|
let sorting = $state<SortingState>([]);
|
||||||
|
let columnFilters = $state<ColumnFiltersState>([]);
|
||||||
|
|
||||||
|
const table = createSvelteTable({
|
||||||
|
get data() {
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
get pagination() {
|
||||||
|
return pagination;
|
||||||
|
},
|
||||||
|
get sorting() {
|
||||||
|
return sorting;
|
||||||
|
},
|
||||||
|
get columnFilters() {
|
||||||
|
return columnFilters;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onPaginationChange: (updater) => {
|
||||||
|
if (typeof updater === "function") {
|
||||||
|
pagination = updater(pagination);
|
||||||
|
} else {
|
||||||
|
pagination = updater;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSortingChange: (updater) => {
|
||||||
|
if (typeof updater === "function") {
|
||||||
|
sorting = updater(sorting);
|
||||||
|
} else {
|
||||||
|
sorting = updater;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onColumnFiltersChange: (updater) => {
|
||||||
|
if (typeof updater === "function") {
|
||||||
|
columnFilters = updater(columnFilters);
|
||||||
|
} else {
|
||||||
|
columnFilters = updater;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
columns,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="rounded-md border m-4">
|
||||||
|
<div class="flex items-center p-4 border-b">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter Players..."
|
||||||
|
value={(table.getColumn("name")?.getFilterValue() as string) ?? ""}
|
||||||
|
onchange={(e) => {
|
||||||
|
table.getColumn("name")?.setFilterValue(e.currentTarget.value);
|
||||||
|
}}
|
||||||
|
oninput={(e) => {
|
||||||
|
table.getColumn("name")?.setFilterValue(e.currentTarget.value);
|
||||||
|
}}
|
||||||
|
class="max-w-sm"
|
||||||
|
/>
|
||||||
|
<div class="flex items-center px-4">
|
||||||
|
<Select type="single" value={pagination.pageSize.toString()} onValueChange={(e) => pagination = { pageSize: +e, pageIndex: 0 }}>
|
||||||
|
<SelectTrigger>{pagination.pageSize}</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="5">5</SelectItem>
|
||||||
|
<SelectItem value="10">10</SelectItem>
|
||||||
|
<SelectItem value="25">25</SelectItem>
|
||||||
|
<SelectItem value="50">50</SelectItem>
|
||||||
|
<SelectItem value="100">100</SelectItem>
|
||||||
|
<SelectItem value="200">200</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
||||||
|
<TableRow>
|
||||||
|
{#each headerGroup.headers as header (header.id)}
|
||||||
|
<TableHead>
|
||||||
|
{#if !header.isPlaceholder}
|
||||||
|
<FlexRender
|
||||||
|
content={header.column.columnDef.header}
|
||||||
|
context={header.getContext()}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</TableHead>
|
||||||
|
{/each}
|
||||||
|
</TableRow>
|
||||||
|
{/each}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{#each table.getRowModel().rows as row (row.id)}
|
||||||
|
<TableRow data-state={row.getIsSelected() && "selected"}>
|
||||||
|
{#each row.getVisibleCells() as cell (cell.id)}
|
||||||
|
<TableCell>
|
||||||
|
<FlexRender
|
||||||
|
content={cell.column.columnDef.cell}
|
||||||
|
context={cell.getContext()}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
{/each}
|
||||||
|
</TableRow>
|
||||||
|
{:else}
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colspan={columns.length} class="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
{/each}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<div class="flex items-center justify-end space-x-2 p-4 border-t">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onclick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</Button>
|
||||||
|
<span>{pagination.pageIndex + 1}/{table.getPageCount()}</span>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onclick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
60
src/components/moderator/pages/players/columns.ts
Normal file
60
src/components/moderator/pages/players/columns.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {ColumnDef} from "@tanstack/table-core";
|
||||||
|
import type {Player} from "@type/data.ts";
|
||||||
|
import { renderComponent } from "@components/ui/data-table";
|
||||||
|
import PermissionsDropdown from "@components/moderator/pages/players/PermissionsDropdown.svelte";
|
||||||
|
import PrefixDropdown from "@components/moderator/pages/players/PrefixDropdown.svelte";
|
||||||
|
|
||||||
|
export const columns: ColumnDef<Player[]> = [
|
||||||
|
{
|
||||||
|
accessorKey: "uuid",
|
||||||
|
header: "UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "name",
|
||||||
|
header: "Name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "prefix",
|
||||||
|
header: "Prefix",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return renderComponent(
|
||||||
|
PrefixDropdown, {
|
||||||
|
prefix: row.getValue("prefix"),
|
||||||
|
uuid: row.getValue("uuid"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "perms",
|
||||||
|
header: "Permissions",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return renderComponent(
|
||||||
|
PermissionsDropdown,
|
||||||
|
{
|
||||||
|
perms: row.getValue("perms"),
|
||||||
|
uuid: row.getValue("uuid"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -20,7 +20,7 @@
|
|||||||
import type {Player, Server} from "@type/data.ts";
|
import type {Player, Server} from "@type/data.ts";
|
||||||
import {PlayerSchema, ServerSchema} from "@type/data.ts";
|
import {PlayerSchema, ServerSchema} from "@type/data.ts";
|
||||||
import {fetchWithToken, tokenStore} from "./repo.ts";
|
import {fetchWithToken, tokenStore} from "./repo.ts";
|
||||||
import {derived} from "svelte/store";
|
import {derived, get} from "svelte/store";
|
||||||
|
|
||||||
export class DataRepo {
|
export class DataRepo {
|
||||||
constructor(private token: string) {
|
constructor(private token: string) {
|
||||||
@ -33,6 +33,10 @@ export class DataRepo {
|
|||||||
public async getMe(): Promise<Player> {
|
public async getMe(): Promise<Player> {
|
||||||
return await fetchWithToken(this.token, "/data/me").then(value => value.json()).then(PlayerSchema.parse);
|
return await fetchWithToken(this.token, "/data/me").then(value => value.json()).then(PlayerSchema.parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getPlayers(): Promise<Player[]> {
|
||||||
|
return await fetchWithToken(get(tokenStore), "/data/admin/users").then(value => value.json()).then(PlayerSchema.array().parse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const dataRepo = derived(tokenStore, ($token) => new DataRepo($token));
|
export const dataRepo = derived(tokenStore, ($token) => new DataRepo($token));
|
||||||
@ -31,15 +31,15 @@ export interface CreateEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateEvent {
|
export interface UpdateEvent {
|
||||||
name: string | null;
|
name?: string | null;
|
||||||
start: Dayjs | null;
|
start?: Dayjs | number | null;
|
||||||
end: Dayjs | null;
|
end?: Dayjs | number | null;
|
||||||
deadline: Dayjs | null;
|
deadline?: Dayjs | number | null;
|
||||||
maxTeamMembers: number | null;
|
maxTeamMembers?: number | null;
|
||||||
schemType: string | null;
|
schemType?: string | null;
|
||||||
publicSchemsOnly: boolean | null;
|
publicSchemsOnly?: boolean | null;
|
||||||
addReferee: string[] | null;
|
addReferee?: string[] | null;
|
||||||
removeReferee: string[] | null;
|
removeReferee?: string[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EventRepo {
|
export class EventRepo {
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import {z} from "zod";
|
|||||||
import {fetchWithToken, tokenStore} from "@repo/repo.ts";
|
import {fetchWithToken, tokenStore} from "@repo/repo.ts";
|
||||||
import {pageRepo} from "@repo/page.ts";
|
import {pageRepo} from "@repo/page.ts";
|
||||||
import {dataRepo} from "@repo/data.ts";
|
import {dataRepo} from "@repo/data.ts";
|
||||||
|
import {permsRepo} from "@repo/perms.ts";
|
||||||
|
|
||||||
export const schemTypes = cached<SchematicType[]>([], () =>
|
export const schemTypes = cached<SchematicType[]>([], () =>
|
||||||
fetchWithToken(get(tokenStore), "/data/admin/schematicTypes")
|
fetchWithToken(get(tokenStore), "/data/admin/schematicTypes")
|
||||||
@ -37,6 +38,13 @@ export const players = cached<Player[]>([], async () => {
|
|||||||
return z.array(PlayerSchema).parse(await res.json());
|
return z.array(PlayerSchema).parse(await res.json());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const permissions = cached({
|
||||||
|
perms: [],
|
||||||
|
prefixes: {},
|
||||||
|
}, async () => {
|
||||||
|
return get(permsRepo).listPerms();
|
||||||
|
});
|
||||||
|
|
||||||
export const gamemodes = cached<string[]>([], async () => {
|
export const gamemodes = cached<string[]>([], async () => {
|
||||||
const res = await fetchWithToken(get(tokenStore), "/data/admin/gamemodes");
|
const res = await fetchWithToken(get(tokenStore), "/data/admin/gamemodes");
|
||||||
return z.array(z.string()).parse(await res.json());
|
return z.array(z.string()).parse(await res.json());
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount} from "svelte";
|
import {onMount} from "svelte";
|
||||||
import {stopPropagation} from "@components/util.ts";
|
import {stopPropagation} from "@components/utils.ts";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
129
src/components/ui/datetime-picker/DateTimePicker.svelte
Normal file
129
src/components/ui/datetime-picker/DateTimePicker.svelte
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<!--
|
||||||
|
- This file is a part of the SteamWar software.
|
||||||
|
-
|
||||||
|
- Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as published by
|
||||||
|
- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
- (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Calendar } from "@components/ui/calendar";
|
||||||
|
import { Button } from "@components/ui/button";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "$lib/components/ui/popover";
|
||||||
|
import { ScrollArea } from "$lib/components/ui/scroll-area";
|
||||||
|
import { CalendarIcon } from "lucide-svelte";
|
||||||
|
import { cn } from "@components/utils";
|
||||||
|
import type {ZonedDateTime} from "@internationalized/date";
|
||||||
|
|
||||||
|
let {
|
||||||
|
value = $bindable(),
|
||||||
|
onChange
|
||||||
|
}: {
|
||||||
|
value: ZonedDateTime
|
||||||
|
onChange?: ((date: ZonedDateTime | undefined) => void) | undefined
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
let isOpen = $state(false);
|
||||||
|
|
||||||
|
const hours = Array.from({ length: 24 }, (_, i) => i);
|
||||||
|
|
||||||
|
function handleDateSelect(selectedDate: ZonedDateTime) {
|
||||||
|
if (selectedDate) {
|
||||||
|
value = selectedDate;
|
||||||
|
if (onChange) {
|
||||||
|
onChange(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTimeChange(type: "hour" | "minute", change: number) {
|
||||||
|
if (change !== undefined) {
|
||||||
|
if (type === "hour") {
|
||||||
|
value = value.set({ hour: change });
|
||||||
|
} else if (type === "minute") {
|
||||||
|
value = value.set({ minute: change });
|
||||||
|
}
|
||||||
|
if (onChange) {
|
||||||
|
onChange(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Popover bind:open={isOpen}>
|
||||||
|
<PopoverTrigger>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
class={cn(
|
||||||
|
"w-full justify-start text-left font-normal",
|
||||||
|
!value && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
{#if value}
|
||||||
|
{new Intl.DateTimeFormat("de-DE", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
year: "numeric",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
}).format(value.toDate())}
|
||||||
|
{:else}
|
||||||
|
<span>DD.MM.YYYY, hh:mm</span>
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
|
||||||
|
<PopoverContent class="w-auto p-0">
|
||||||
|
<div class="sm:flex">
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
bind:value
|
||||||
|
onValueChange={(date) => handleDateSelect(date)}
|
||||||
|
initialFocus
|
||||||
|
/>
|
||||||
|
<div class="flex flex-col sm:flex-row sm:h-[300px] divide-y sm:divide-y-0 sm:divide-x">
|
||||||
|
<ScrollArea class="w-64 sm:w-auto">
|
||||||
|
<div class="flex sm:flex-col p-2">
|
||||||
|
{#each [...hours].reverse() as hour}
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant={value && value.hour === hour ? "default" : "ghost"}
|
||||||
|
class="sm:w-full shrink-0 aspect-square"
|
||||||
|
onclick={() => handleTimeChange("hour", hour)}
|
||||||
|
>
|
||||||
|
{hour}
|
||||||
|
</Button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
|
<ScrollArea class="w-64 sm:w-auto">
|
||||||
|
<div class="flex sm:flex-col p-2">
|
||||||
|
{#each Array.from({ length: 60 }, (_, i) => i) as minute}
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant={value && value.minute === minute ? "default" : "ghost"}
|
||||||
|
class="sm:w-full shrink-0 aspect-square"
|
||||||
|
onclick={() => handleTimeChange("minute", minute)}
|
||||||
|
>
|
||||||
|
{minute.toString().padStart(2, '0')}
|
||||||
|
</Button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
import "$lib/styles/app.css";
|
||||||
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";
|
||||||
|
|||||||
9
src/pages/admin/new.astro
Normal file
9
src/pages/admin/new.astro
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
import Basic from "../../layouts/Basic.astro";
|
||||||
|
import App from "@components/moderator/App.svelte";
|
||||||
|
---
|
||||||
|
|
||||||
|
<Basic clientSideRouter={false}>
|
||||||
|
<App client:only="svelte"/>
|
||||||
|
</Basic>
|
||||||
97
src/styles/app.css
Normal file
97
src/styles/app.css
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--muted: 240 4.8% 95.9%;
|
||||||
|
--muted-foreground: 240 3.8% 46.1%;
|
||||||
|
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--border: 240 5.9% 90%;
|
||||||
|
--input: 240 5.9% 90%;
|
||||||
|
|
||||||
|
--primary: 240 5.9% 10%;
|
||||||
|
--primary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--secondary: 240 4.8% 95.9%;
|
||||||
|
--secondary-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--accent: 240 4.8% 95.9%;
|
||||||
|
--accent-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--ring: 240 5% 64.9%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 240 10% 3.9%;
|
||||||
|
--foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--muted: 240 3.7% 15.9%;
|
||||||
|
--muted-foreground: 240 5% 64.9%;
|
||||||
|
|
||||||
|
--popover: 240 10% 3.9%;
|
||||||
|
--popover-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--card: 240 10% 3.9%;
|
||||||
|
--card-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--border: 240 3.7% 15.9%;
|
||||||
|
--input: 240 3.7% 15.9%;
|
||||||
|
|
||||||
|
--primary: 0 0% 98%;
|
||||||
|
--primary-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--secondary: 240 3.7% 15.9%;
|
||||||
|
--secondary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--accent: 240 3.7% 15.9%;
|
||||||
|
--accent-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 0 85.7% 97.3%;
|
||||||
|
|
||||||
|
--ring: 240 3.7% 15.9%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,20 +0,0 @@
|
|||||||
const defaultTheme = require("tailwindcss/defaultTheme");
|
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
module.exports = {
|
|
||||||
content: [
|
|
||||||
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
|
|
||||||
"./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}"
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
primary: { 50: '#FFF5F2', 100: '#FFF1EE', 200: '#FFE4DE', 300: '#FFD5CC', 400: '#FFBCAD', 500: '#FE795D', 600: '#EF562F', 700: '#EB4F27', 800: '#CC4522', 900: '#A5371B'},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
require('flowbite/plugin')
|
|
||||||
],
|
|
||||||
darkMode: 'class'
|
|
||||||
}
|
|
||||||
86
tailwind.config.js
Normal file
86
tailwind.config.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { fontFamily } from "tailwindcss/defaultTheme";
|
||||||
|
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
const config = {
|
||||||
|
darkMode: ["class"],
|
||||||
|
content: [
|
||||||
|
"./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}",
|
||||||
|
"./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}",
|
||||||
|
],
|
||||||
|
safelist: ["dark"],
|
||||||
|
theme: {
|
||||||
|
container: {
|
||||||
|
center: true,
|
||||||
|
padding: "2rem",
|
||||||
|
screens: {
|
||||||
|
"2xl": "1400px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
border: "hsl(var(--border) / <alpha-value>)",
|
||||||
|
input: "hsl(var(--input) / <alpha-value>)",
|
||||||
|
ring: "hsl(var(--ring) / <alpha-value>)",
|
||||||
|
background: "hsl(var(--background) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--foreground) / <alpha-value>)",
|
||||||
|
primary: {
|
||||||
|
DEFAULT: "hsl(var(--primary) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--primary-foreground) / <alpha-value>)",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--secondary-foreground) / <alpha-value>)",
|
||||||
|
},
|
||||||
|
destructive: {
|
||||||
|
DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)",
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
DEFAULT: "hsl(var(--muted) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--muted-foreground) / <alpha-value>)",
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
DEFAULT: "hsl(var(--accent) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--accent-foreground) / <alpha-value>)",
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
DEFAULT: "hsl(var(--popover) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--popover-foreground) / <alpha-value>)",
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
DEFAULT: "hsl(var(--card) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--card-foreground) / <alpha-value>)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
lg: "var(--radius)",
|
||||||
|
md: "calc(var(--radius) - 2px)",
|
||||||
|
sm: "calc(var(--radius) - 4px)",
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: [...fontFamily.sans],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@ -20,6 +20,8 @@
|
|||||||
"@repo": ["./src/components/repo"],
|
"@repo": ["./src/components/repo"],
|
||||||
"@stores/*": ["./src/components/stores/*"],
|
"@stores/*": ["./src/components/stores/*"],
|
||||||
"@stores": ["./src/components/stores"],
|
"@stores": ["./src/components/stores"],
|
||||||
|
"$lib/*": ["./src/*"],
|
||||||
|
"$lib": ["./src"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user