This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import Icon from '@iconify/svelte';
|
||||
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
|
||||
import { addAdmin, fetchAdmins } from '@app/admin/admins/admins.ts';
|
||||
import { Permissions } from '@util/permissions.ts';
|
||||
@@ -15,7 +14,7 @@
|
||||
|
||||
<div>
|
||||
<button class="btn btn-soft w-full" onclick={() => (createPopupOpen = true)}>
|
||||
<Icon icon="heroicons:plus-16-solid" />
|
||||
<span class="iconify iconify-[heroicons--plus-16-solid]"></span>
|
||||
<span>Neuer Admin</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import Icon from '@iconify/svelte';
|
||||
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
|
||||
import { fetchBlockedUsers, addBlockedUser } from '@app/admin/blockedUsers/blockedUsers.ts';
|
||||
|
||||
@@ -14,7 +13,7 @@
|
||||
|
||||
<div>
|
||||
<button class="btn btn-soft w-full" onclick={() => (createPopupOpen = true)}>
|
||||
<Icon icon="heroicons:plus-16-solid" />
|
||||
<span class="iconify iconify-[heroicons--plus-16-solid]"></span>
|
||||
<span>Neuer blockierter Nutzer</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import Icon from '@iconify/svelte';
|
||||
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
|
||||
import { addDirectInvitation, fetchDirectInvitations } from '@app/admin/directInvitations/directInvitations.ts';
|
||||
|
||||
@@ -14,7 +13,7 @@
|
||||
|
||||
<div>
|
||||
<button class="btn btn-soft w-full" onclick={() => (createPopupOpen = true)}>
|
||||
<Icon icon="heroicons:plus-16-solid" />
|
||||
<span class="iconify iconify-[heroicons--plus-16-solid]"></span>
|
||||
<span>Neue direkte Einladung</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import Select from '@components/input/Select.svelte';
|
||||
import { editReportStatus, getReportStatus } from '@app/admin/reports/reports.ts';
|
||||
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
|
||||
import Icon from '@iconify/svelte';
|
||||
import UserSearch from '@components/admin/search/UserSearch.svelte';
|
||||
|
||||
// html bindings
|
||||
@@ -90,7 +89,9 @@
|
||||
>
|
||||
<div class="absolute right-2 top-2">
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn btn-sm btn-circle btn-ghost"><Icon icon="heroicons:share" /></div>
|
||||
<div tabindex="0" role="button" class="btn btn-sm btn-circle btn-ghost">
|
||||
<span class="iconify iconify-[heroicons--share]"></span>
|
||||
</div>
|
||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
||||
<ul tabindex="0" class="menu dropdown-content bg-base-100 rounded-box z-1 p-2 shadow-sm w-max">
|
||||
<li><button onclick={() => onCopyPublicLink(report?.urlHash)}>Öffentlichen Report Link kopieren</button></li>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import Icon from '@iconify/svelte';
|
||||
import Input from '@components/input/Input.svelte';
|
||||
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
|
||||
import { addReport, fetchReports } from '@app/admin/reports/reports.ts';
|
||||
@@ -27,7 +26,7 @@
|
||||
</fieldset>
|
||||
<div class="divider my-1"></div>
|
||||
<button class="btn btn-soft w-full" onclick={() => (createPopupOpen = true)}>
|
||||
<Icon icon="heroicons:plus-16-solid" />
|
||||
<span class="iconify iconify-[heroicons--plus-16-solid]"></span>
|
||||
<span>Neuer Report</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script>
|
||||
import Icon from '@iconify/svelte';
|
||||
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
|
||||
import { addStrikeReason, fetchStrikeReasons } from '@app/admin/strikeReasons/strikeReasons.js';
|
||||
|
||||
@@ -14,7 +13,7 @@
|
||||
|
||||
<div>
|
||||
<button class="btn btn-soft w-full" onclick={() => (createPopupOpen = true)}>
|
||||
<Icon icon="heroicons:plus-16-solid" />
|
||||
<span class="iconify iconify-[heroicons--plus-16-solid]"></span>
|
||||
<span>Neuer Strikegrund</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import Icon from '@iconify/svelte';
|
||||
import Input from '@components/input/Input.svelte';
|
||||
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
|
||||
import { addUser, fetchUsers } from '@app/admin/users/users.ts';
|
||||
@@ -22,7 +21,7 @@
|
||||
</fieldset>
|
||||
<div class="divider my-1"></div>
|
||||
<button class="btn btn-soft w-full" onclick={() => (createPopupOpen = true)}>
|
||||
<Icon icon="heroicons:plus-16-solid" />
|
||||
<span class="iconify iconify-[heroicons--plus-16-solid]"></span>
|
||||
<span>Neuer Nutzer</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
60
src/app/website/index/LiveStats.svelte
Normal file
60
src/app/website/index/LiveStats.svelte
Normal file
@@ -0,0 +1,60 @@
|
||||
<script lang="ts">
|
||||
import { STATISTICS_INTERVAL } from 'astro:env/server';
|
||||
import { statistics } from '@app/website/index/liveStats.ts';
|
||||
|
||||
function transformPlaytimeMinutes(playtimeMinutes: number) {
|
||||
return playtimeMinutes < 60 * 24
|
||||
? `${Math.floor(playtimeMinutes / 60)} Stunden`
|
||||
: `${Math.floor(playtimeMinutes / 60 / 24)} Tage`;
|
||||
}
|
||||
|
||||
function transformMobKills(mobKills: number) {
|
||||
return mobKills < 1000 ? `${mobKills}` : `${Math.floor(mobKills / 1000)}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="flex flex-row items-start mb-6">
|
||||
<div class="tooltip" data-tip="* Die Statistiken werden alle {STATISTICS_INTERVAL / 60} Minuten aktualisiert">
|
||||
<h3 class="text-2xl">Live<span class="font-geist text-[18px]">*</span> Statistiken</h3>
|
||||
</div>
|
||||
<div class="inline-grid *:[grid-area:1/1] mt-1 ml-0.5">
|
||||
<div class="status status-info animate-ping"></div>
|
||||
<div class="status status-info"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col lg:flex-row gap-4">
|
||||
<div class="bg-base-200 stats stats-vertical xl:stats-horizontal shadow h-min xl:h-[initial]">
|
||||
<div class="stat">
|
||||
<div class="stat-figure">
|
||||
<span class="iconify iconify-[heroicons--clock-solid] size-[1.5em]"></span>
|
||||
</div>
|
||||
<div class="stat-title">Gesamtspielzeit</div>
|
||||
<div class="stat-value">
|
||||
{statistics != null ? transformPlaytimeMinutes(statistics.playtimeMinutes) : 'n/a'}
|
||||
</div>
|
||||
<div class="stat-desc">​</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-base-200 stats stats-vertical xl:stats-horizontal shadow">
|
||||
<div class="stat">
|
||||
<div class="stat-figure">
|
||||
<span class="iconify iconify-[local--crosshairs] size-[1.5em]"></span>
|
||||
</div>
|
||||
<div class="stat-title">Getötete Monster</div>
|
||||
<div class="stat-value">{statistics != null ? transformMobKills(statistics.mobKills) : 'n/a'}</div>
|
||||
<div class="stat-desc">​</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-figure">
|
||||
<span class="iconify iconify-[local--skull] size-[1.5em]"></span>
|
||||
</div>
|
||||
<div class="stat-title">Spieler Tode</div>
|
||||
<div class="stat-value">{statistics?.playerDeaths ?? 'n/a'}</div>
|
||||
<div class="stat-desc">
|
||||
<span class="underline">{statistics?.playerKills ?? 'n/a'}</span> davon durch andere Spieler
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
67
src/app/website/index/liveStats.ts
Normal file
67
src/app/website/index/liveStats.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { STATISTICS_ENDPOINT, STATISTICS_INTERVAL } from 'astro:env/server';
|
||||
import { logger } from '@util/log.ts';
|
||||
|
||||
export let statistics: Awaited<ReturnType<typeof fetchStatistics>> | null = null;
|
||||
if (STATISTICS_ENDPOINT) {
|
||||
statistics = await fetchStatistics().catch((_) => null);
|
||||
setInterval(
|
||||
() =>
|
||||
fetchStatistics()
|
||||
.catch((_) => null)
|
||||
.then((val) => (statistics = val)),
|
||||
STATISTICS_INTERVAL * 60 * 1000
|
||||
);
|
||||
}
|
||||
|
||||
async function fetchStatistics() {
|
||||
const response = (await fetch(STATISTICS_ENDPOINT!, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
categories: [{ name: 'PLAY_ONE_MINUTE' }, { name: 'PLAYER_KILLS' }, { name: 'DEATHS' }, { name: 'MOB_KILLS' }]
|
||||
})
|
||||
}).catch((e) => {
|
||||
logger.warn(
|
||||
{
|
||||
error: e
|
||||
},
|
||||
'could not fetch statistics'
|
||||
);
|
||||
throw e;
|
||||
})) as Response;
|
||||
|
||||
type StatisticName = 'PLAY_ONE_MINUTE' | 'PLAYER_KILLS' | 'DEATHS' | 'MOB_KILLS';
|
||||
type ResponseData = {
|
||||
response: {
|
||||
playerStatistics: {
|
||||
playerName: string;
|
||||
statistics: { name: StatisticName; value: number }[];
|
||||
}[];
|
||||
};
|
||||
};
|
||||
const responseData: ResponseData = await response.json();
|
||||
|
||||
let playtimeMinutes = 0;
|
||||
let playerKills = 0;
|
||||
let playerDeaths = 0;
|
||||
let mobKills = 0;
|
||||
for (const player of responseData.response.playerStatistics) {
|
||||
for (const statistic of player.statistics) {
|
||||
switch (statistic.name) {
|
||||
case 'PLAY_ONE_MINUTE':
|
||||
playtimeMinutes += statistic.value / 20 / 60;
|
||||
break;
|
||||
case 'PLAYER_KILLS':
|
||||
playerKills += statistic.value;
|
||||
break;
|
||||
case 'DEATHS':
|
||||
playerDeaths += statistic.value;
|
||||
break;
|
||||
case 'MOB_KILLS':
|
||||
mobKills += statistic.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { playtimeMinutes, playerKills, playerDeaths, mobKills };
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
import type { Writable } from 'svelte/store';
|
||||
import SortableTr from '@components/admin/table/SortableTr.svelte';
|
||||
import SortableTh from '@components/admin/table/SortableTh.svelte';
|
||||
import Icon from '@iconify/svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { getObjectEntryByKey } from '@util/objects.ts';
|
||||
|
||||
@@ -64,12 +63,12 @@
|
||||
<td>
|
||||
{#if onEdit}
|
||||
<button class="cursor-pointer" onclick={() => onEdit(d)}>
|
||||
<Icon icon="heroicons:pencil-square" />
|
||||
<span class="iconify iconify-[heroicons--pencil-square]"></span>
|
||||
</button>
|
||||
{/if}
|
||||
{#if onDelete}
|
||||
<button class="cursor-pointer" onclick={() => onDelete(d)}>
|
||||
<Icon icon="heroicons:trash" />
|
||||
<span class="iconify iconify-[heroicons--trash]"></span>
|
||||
</button>
|
||||
{/if}
|
||||
</td>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { getContext, type Snippet } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import Icon from '@iconify/svelte';
|
||||
|
||||
// types
|
||||
interface Props {
|
||||
@@ -37,9 +36,9 @@
|
||||
<button class="flex items-center gap-1" onclick={() => onButtonClick()}>
|
||||
<span>{@render children?.()}</span>
|
||||
{#if $headerKey === key && asc}
|
||||
<Icon icon="heroicons:chevron-up-16-solid" />
|
||||
<span class="iconify iconify-[heroicons--chevron-up-16-solid]"></span>
|
||||
{:else}
|
||||
<Icon icon="heroicons:chevron-down-16-solid" />
|
||||
<span class="iconify iconify-[heroicons--chevron-down-16-solid]"></span>
|
||||
{/if}
|
||||
</button>
|
||||
{:else}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
import Icon from '@iconify/svelte';
|
||||
|
||||
interface Props {
|
||||
id?: string;
|
||||
@@ -44,19 +43,21 @@
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Hide password"
|
||||
class="absolute right-2 cursor-pointer z-10"
|
||||
class:hidden={!visible}
|
||||
onclick={() => (visible = !visible)}
|
||||
>
|
||||
<Icon icon="heroicons:eye-16-solid" width={22} />
|
||||
<span class="iconify iconify-[heroicons--eye-16-solid] w-[22px]"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Show password"
|
||||
class="absolute right-2 cursor-pointer z-10"
|
||||
class:hidden={visible}
|
||||
onclick={() => (visible = !visible)}
|
||||
>
|
||||
<Icon icon="heroicons:eye-slash-16-solid" width={22} />
|
||||
<span class="iconify iconify-[heroicons--eye-slash-16-solid] w-[22px]"></span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="fieldset-label">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
import '@styles/global.css';
|
||||
import { ClientRouter } from 'astro:transitions';
|
||||
import { BASE_PATH } from 'astro:env/server';
|
||||
import logo512 from '@assets/img/logo-512.webp';
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
---
|
||||
import '@assets/admin_layout.css';
|
||||
import '@styles/adminLayout.css';
|
||||
import BaseLayout from '../BaseLayout.astro';
|
||||
import { ClientRouter } from 'astro:transitions';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import Popup from '@components/popup/Popup.svelte';
|
||||
import ConfirmPopup from '@components/popup/ConfirmPopup.svelte';
|
||||
import { Session } from '@util/session.ts';
|
||||
@@ -19,24 +18,24 @@ const preTabs = [
|
||||
{
|
||||
href: '',
|
||||
name: 'Startseite',
|
||||
icon: 'heroicons:computer-desktop-20-solid'
|
||||
iconClass: 'iconify-[heroicons--computer-desktop-20-solid]'
|
||||
}
|
||||
];
|
||||
const adminTabs = [
|
||||
{
|
||||
href: 'admin/users',
|
||||
name: 'Registrierte Nutzer',
|
||||
icon: 'heroicons:user',
|
||||
iconClass: 'iconify-[heroicons--user]',
|
||||
subTabs: [
|
||||
{
|
||||
href: 'admin/users/direct_invitations',
|
||||
name: 'Direkte Einladungen',
|
||||
icon: 'heroicons:envelope'
|
||||
iconClass: 'iconify-[heroicons--envelope]'
|
||||
},
|
||||
{
|
||||
href: 'admin/users/blocked',
|
||||
name: 'Blockierte Nutzer',
|
||||
icon: 'heroicons:user-minus'
|
||||
iconClass: 'iconify-[heroicons--user-minus]'
|
||||
}
|
||||
],
|
||||
enabled: session?.permissions.users
|
||||
@@ -44,24 +43,24 @@ const adminTabs = [
|
||||
{
|
||||
href: 'admin/reports',
|
||||
name: 'Reports',
|
||||
icon: 'heroicons:flag',
|
||||
iconClass: 'iconify-[heroicons--flag]',
|
||||
enabled: session?.permissions.reports
|
||||
},
|
||||
{
|
||||
href: 'admin/feedback',
|
||||
name: 'Feedback',
|
||||
icon: 'heroicons:book-open',
|
||||
iconClass: 'iconify-[heroicons--book-open]',
|
||||
enabled: session?.permissions.feedback
|
||||
},
|
||||
{
|
||||
href: 'admin/admins',
|
||||
name: 'Website Admins',
|
||||
icon: 'heroicons:code-bracket-16-solid',
|
||||
iconClass: 'iconify-[heroicons--code-bracket-16-solid]',
|
||||
subTabs: [
|
||||
{
|
||||
href: 'admin/admins/strike_reasons',
|
||||
name: 'Strikegründe',
|
||||
icon: 'heroicons:shield-exclamation'
|
||||
iconClass: 'iconify-[heroicons--shield-exclamation]'
|
||||
}
|
||||
],
|
||||
enabled: session?.permissions.admin
|
||||
@@ -69,13 +68,13 @@ const adminTabs = [
|
||||
{
|
||||
href: 'admin/settings',
|
||||
name: 'Einstellungen',
|
||||
icon: 'heroicons:adjustments-horizontal',
|
||||
iconClass: 'iconify-[heroicons--adjustments-horizontal]',
|
||||
enabled: session?.permissions.settings
|
||||
},
|
||||
{
|
||||
href: 'admin/tools',
|
||||
name: 'Tools',
|
||||
icon: 'heroicons:wrench-screwdriver',
|
||||
iconClass: 'iconify-[heroicons--wrench-screwdriver]',
|
||||
enabled: session?.permissions.tools
|
||||
}
|
||||
];
|
||||
@@ -89,7 +88,7 @@ const adminTabs = [
|
||||
preTabs.map((tab) => (
|
||||
<li>
|
||||
<a href={tab.href}>
|
||||
<Icon name={tab.icon} />
|
||||
<span class="iconify" class:list={tab.iconClass} />
|
||||
<span>{tab.name}</span>
|
||||
</a>
|
||||
</li>
|
||||
@@ -102,7 +101,7 @@ const adminTabs = [
|
||||
tab.enabled && (
|
||||
<li>
|
||||
<a href={tab.href}>
|
||||
<Icon name={tab.icon} />
|
||||
<span class="iconify" class:list={tab.iconClass} />
|
||||
<span>{tab.name}</span>
|
||||
</a>
|
||||
{tab.subTabs && (
|
||||
@@ -110,7 +109,7 @@ const adminTabs = [
|
||||
{tab.subTabs.map((subTab) => (
|
||||
<li>
|
||||
<a href={subTab.href}>
|
||||
<Icon name={subTab.icon} />
|
||||
<span class="iconify" class:list={subTab.iconClass} />
|
||||
<span>{subTab.name}</span>
|
||||
</a>
|
||||
</li>
|
||||
@@ -130,7 +129,7 @@ const adminTabs = [
|
||||
}
|
||||
<li class:list={[Astro.slots.has('actions') ? null : 'mt-auto']}>
|
||||
<button id="logout">
|
||||
<Icon name="heroicons:arrow-left-end-on-rectangle" />
|
||||
<span class="iconify iconify-[heroicons--arrow-left-end-on-rectangle]"></span>
|
||||
<span>Ausloggen</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import '@assets/admin_layout.css';
|
||||
import '@styles/adminLayout.css';
|
||||
import BaseLayout from '../BaseLayout.astro';
|
||||
import { ClientRouter } from 'astro:transitions';
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
import '@assets/website_layout.css';
|
||||
import '@styles/websiteLayout.css';
|
||||
import BaseLayout from '../BaseLayout.astro';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import Menu from '@app/layout/Menu.svelte';
|
||||
|
||||
interface Props {
|
||||
@@ -32,10 +31,10 @@ const { title, description, footer = true } = Astro.props;
|
||||
</div>
|
||||
<div class="hidden sm:flex gap-2 items-center">
|
||||
<a href="ts3server://mhsl.eu?port=9987" title="TeamSpeak" target="_blank">
|
||||
<Icon name="fa-brands:teamspeak" />
|
||||
<span class="iconify iconify-[fa-brands--teamspeak]"></span>
|
||||
</a>
|
||||
<a href="https://discord.gg/EBGefWPc2K" title="Discord" target="_blank">
|
||||
<Icon name="fa-brands:discord" />
|
||||
<span class="iconify iconify-[fa-brands--discord]"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@ import Popup from '@components/popup/Popup.svelte';
|
||||
<form id="login" class="flex flex-col items-center">
|
||||
<div>
|
||||
<Input id="username" type="text" label="Nutzername" required />
|
||||
<Password id="password" label="Passwort" required />
|
||||
<Password id="password" label="Passwort" required client:load />
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button class="btn btn-neutral">Login</button>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
---
|
||||
import WebsiteLayout from '@layouts/website/WebsiteLayout.astro';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
const team = [
|
||||
{
|
||||
name: 'Elias',
|
||||
nickname: 'MineTec',
|
||||
roles: ['Gründer', 'Support', 'Organisation', 'Softwareentwicklung', 'Systemadministrator'],
|
||||
links: [{ name: 'Website', href: 'https://mhsl.eu/aboutme/', icon: 'heroicons:globe-alt-solid' }]
|
||||
links: [{ name: 'Website', href: 'https://mhsl.eu/aboutme/', iconClass: 'iconify-[heroicons--globe-alt-solid]' }]
|
||||
},
|
||||
{
|
||||
name: 'Jannik',
|
||||
@@ -23,13 +22,19 @@ const team = [
|
||||
name: 'Ruben',
|
||||
nickname: 'bytedream',
|
||||
roles: ['Softwareentwicklung'],
|
||||
links: [{ name: 'Website', href: 'https://bytedream.dev', icon: 'heroicons:globe-alt-solid' }]
|
||||
links: [{ name: 'Website', href: 'https://bytedream.dev', iconClass: 'iconify-[heroicons--globe-alt-solid]' }]
|
||||
},
|
||||
{
|
||||
name: 'Lars',
|
||||
nickname: '28Pupsi28',
|
||||
roles: ['Support', 'Softwareentwicklung'],
|
||||
links: [{ name: 'Website', href: 'https://mathemann.ddns.net/turtle_game/', icon: 'heroicons:globe-alt-solid' }]
|
||||
links: [
|
||||
{
|
||||
name: 'Website',
|
||||
href: 'https://mathemann.ddns.net/turtle_game/',
|
||||
iconClass: 'iconify-[heroicons--globe-alt-solid]'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Hanad',
|
||||
@@ -73,7 +78,7 @@ const team = [
|
||||
target="_blank"
|
||||
title={link.name}
|
||||
>
|
||||
<Icon name={link.icon} size={22} />
|
||||
<span class="iconify size-[22px]" class:list={[link.iconClass]} />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
import WebsiteLayout from '@layouts/website/WebsiteLayout.astro';
|
||||
import Countdown from '@app/website/index/Countdown.svelte';
|
||||
import LiveStats from '@app/website/index/LiveStats.svelte';
|
||||
import Craftattack from '@assets/img/craftattack.webp';
|
||||
import Background from '@assets/img/background.webp';
|
||||
import { START_DATE } from 'astro:env/server';
|
||||
import { START_DATE, STATISTICS_ENDPOINT } from 'astro:env/server';
|
||||
import { getSetting, SettingKey } from '@util/settings';
|
||||
import { db } from '@db/database.ts';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
const signupEnabled = await getSetting(db, SettingKey.SignupEnabled, false);
|
||||
const signupInfoMessage = await getSetting(db, SettingKey.SignupInfoMessage);
|
||||
@@ -96,14 +96,22 @@ const information = [
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col xl:flex-row justify-center items-center py-20 bg-base-100">
|
||||
<div class="flex flex-col justify-center items-center py-20 bg-base-100">
|
||||
{
|
||||
STATISTICS_ENDPOINT && (
|
||||
<>
|
||||
<LiveStats />
|
||||
<div class="divider divider-horizontal mx-auto my-6 h-18" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
<div>
|
||||
<h3 class="text-center text-2xl mb-6">2024/2025 in Zahlen</h3>
|
||||
<div class="flex flex-col lg:flex-row gap-4">
|
||||
<div class="stats stats-vertical xl:stats-horizontal shadow">
|
||||
<div class="bg-base-200 stats stats-vertical xl:stats-horizontal shadow">
|
||||
<div class="stat">
|
||||
<div class="stat-figure">
|
||||
<Icon name="heroicons:wrench-screwdriver-solid" size="1.5em" />
|
||||
<span class="iconify iconify-[heroicons--wrench-screwdriver-solid] size-[1.5em]"></span>
|
||||
</div>
|
||||
<div class="stat-title">Abgebaute Blöcke</div>
|
||||
<div class="stat-value">17M</div>
|
||||
@@ -111,27 +119,27 @@ const information = [
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-figure">
|
||||
<Icon name="heroicons:users-solid" size="1.5em" />
|
||||
<span class="iconify iconify-[heroicons--users-solid] size-[1.5em]"></span>
|
||||
</div>
|
||||
<div class="stat-title">Teilnehmer</div>
|
||||
<div class="stat-value">161</div>
|
||||
<div class="stat-desc">​</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats stats-vertical xl:stats-horizontal shadow h-min xl:h-[initial]">
|
||||
<div class="bg-base-200 stats stats-vertical xl:stats-horizontal shadow h-min xl:h-[initial]">
|
||||
<div class="stat">
|
||||
<div class="stat-figure">
|
||||
<Icon name="heroicons:clock-solid" size="1.5em" />
|
||||
<span class="iconify iconify-[heroicons--clock-solid] size-[1.5em]"></span>
|
||||
</div>
|
||||
<div class="stat-title">Gesamtspielzeit</div>
|
||||
<div class="stat-value">276 Tage</div>
|
||||
<div class="stat-desc">​</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats stats-vertical xl:stats-horizontal shadow">
|
||||
<div class="bg-base-200 stats stats-vertical xl:stats-horizontal shadow">
|
||||
<div class="stat">
|
||||
<div class="stat-figure">
|
||||
<Icon name="crosshairs" size="1.5em" />
|
||||
<span class="iconify iconify-[local--crosshairs] size-[1.5em]"></span>
|
||||
</div>
|
||||
<div class="stat-title">Getötete Monster</div>
|
||||
<div class="stat-value">751K</div>
|
||||
@@ -139,7 +147,7 @@ const information = [
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-figure">
|
||||
<Icon name="skull" size="1.5em" />
|
||||
<span class="iconify iconify-[local--skull] size-[1.5em]"></span>
|
||||
</div>
|
||||
<div class="stat-title">Spieler Tode</div>
|
||||
<div class="stat-value">2468</div>
|
||||
|
||||
@@ -8,17 +8,17 @@
|
||||
|
||||
@font-face {
|
||||
font-family: Geist;
|
||||
src: url('fonts/Geist.ttf') format('truetype');
|
||||
src: url('../assets/fonts/Geist.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: GeistMono;
|
||||
src: url('fonts/GeistMono.ttf') format('truetype');
|
||||
src: url('../assets/fonts/GeistMono.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Minecraft;
|
||||
src: url('./fonts/MinecraftRegular.otf') format('opentype');
|
||||
src: url('../assets/fonts/MinecraftRegular.otf') format('opentype');
|
||||
}
|
||||
|
||||
@theme {
|
||||
12
src/styles/global.css
Normal file
12
src/styles/global.css
Normal file
@@ -0,0 +1,12 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@plugin "@iconify/tailwind4" {
|
||||
prefix: iconify;
|
||||
icon-sets: from-folder(local, './src/icons');
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.iconify {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
@@ -8,17 +8,17 @@
|
||||
|
||||
@font-face {
|
||||
font-family: Geist;
|
||||
src: url('fonts/Geist.ttf') format('truetype');
|
||||
src: url('../assets/fonts/Geist.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: GeistMono;
|
||||
src: url('fonts/GeistMono.ttf') format('truetype');
|
||||
src: url('../assets/fonts/GeistMono.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Minecraft;
|
||||
src: url('./fonts/MinecraftRegular.otf') format('opentype');
|
||||
src: url('../assets/fonts/MinecraftRegular.otf') format('opentype');
|
||||
}
|
||||
|
||||
@theme {
|
||||
Reference in New Issue
Block a user