Compare commits

...

3 Commits

Author SHA1 Message Date
8b18623232 add blocked user
All checks were successful
deploy / build-and-deploy (push) Successful in 15s
2025-05-20 23:34:54 +02:00
ba1146facf update database foreign key constraints 2025-05-20 20:35:24 +02:00
7e6a09563a move components 2025-05-20 20:34:50 +02:00
28 changed files with 418 additions and 40 deletions

View File

@ -66,6 +66,17 @@ export const signup = {
}); });
} }
// check if user is blocked
if (uuid) {
const blockedUser = await db.getBlockedUserByUuid({ uuid: uuid });
if (blockedUser) {
throw new ActionError({
code: 'FORBIDDEN',
message: 'Du bist für die Registrierung gesperrt'
});
}
}
if (!teamDraft) { if (!teamDraft) {
// check if a team with the same name already exists // check if a team with the same name already exists
if (input.teamName) { if (input.teamName) {

View File

@ -84,5 +84,41 @@ export const user = {
users: users users: users
}; };
} }
}),
addBlocked: defineAction({
input: z.object({
uuid: z.string(),
comment: z.string().nullable()
}),
handler: async (input, context) => {
Session.actionSessionFromCookies(context.cookies, Permissions.Users);
const { id } = await db.addBlockedUser(input);
return {
id: id
};
}
}),
editBlocked: defineAction({
input: z.object({
id: z.number(),
uuid: z.string(),
comment: z.string().nullable()
}),
handler: async (input, context) => {
Session.actionSessionFromCookies(context.cookies, Permissions.Users);
await db.editBlockedUser(input);
}
}),
blocked: defineAction({
handler: async (_, context) => {
Session.actionSessionFromCookies(context.cookies, Permissions.Users);
return {
blocked: await db.getBlockedUsers({})
};
}
}) })
}; };

View File

@ -0,0 +1,101 @@
<script lang="ts">
import Input from '@components/input/Input.svelte';
import Textarea from '@components/input/Textarea.svelte';
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
import type { BlockedUser } from '@app/admin/usersBlocked/types.ts';
import { blockedUserCreateOrEditPopupState } from '@app/admin/usersBlocked/state.ts';
import { onDestroy } from 'svelte';
// html bindings
let modal: HTMLDialogElement;
let modalForm: HTMLFormElement;
// states
let action = $state<'create' | 'edit' | null>(null);
let blockedUser = $state({} as BlockedUser);
let onUpdate = $state((_: BlockedUser) => {});
let submitEnabled = $derived(!!blockedUser.uuid);
// lifecycle
const cancel = blockedUserCreateOrEditPopupState.subscribe((value) => {
if (value && 'create' in value) {
action = 'create';
blockedUser = {
id: -1,
uuid: '',
comment: null
};
onUpdate = value?.create.onUpdate;
modal.show();
} else if (value && 'edit' in value) {
action = 'edit';
blockedUser = value.edit.blockedUser;
onUpdate = value.edit.onUpdate;
modal.show();
}
});
onDestroy(cancel);
// texts
const texts = {
create: {
title: 'Blockierten Nutzer erstellen',
buttonTitle: 'Erstellen',
confirmPopupTitle: 'Nutzer blockieren?',
confirmPopupMessage:
'Bist du sicher, dass der Nutzer blockiert werden soll?\nEin blockierter Nutzer kann sich nicht mehr registrieren.'
},
edit: {
title: 'Blockierten Nutzer bearbeiten',
buttonTitle: 'Speichern',
confirmPopupTitle: 'Änderungen speichern?',
confirmPopupMessage: 'Sollen die Änderungen gespeichert werden?'
},
null: {}
};
// callbacks
async function onSaveButtonClick(e: Event) {
e.preventDefault();
$confirmPopupState = {
title: texts[action!].confirmPopupTitle,
message: texts[action!].confirmPopupMessage,
onConfirm: () => {
modalForm.submit();
onUpdate(blockedUser);
}
};
}
function onCancelButtonClick(e: Event) {
e.preventDefault();
modalForm.submit();
}
</script>
<dialog class="modal" bind:this={modal}>
<form method="dialog" class="modal-box" bind:this={modalForm}>
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" onclick={onCancelButtonClick}>✕</button>
<div class="space-y-5">
<h3 class="text-xt font-geist font-bold">{texts[action!].title}</h3>
<div>
<Input bind:value={blockedUser.uuid} label="UUID" required dynamicWidth />
<Textarea label="Kommentar" bind:value={blockedUser.comment} rows={3} dynamicWidth />
</div>
<div>
<button
class="btn btn-success"
class:disabled={!submitEnabled}
disabled={!submitEnabled}
onclick={onSaveButtonClick}>{texts[action!].buttonTitle}</button
>
<button class="btn btn-error" onclick={onCancelButtonClick}>Abbrechen</button>
</div>
</div>
</form>
<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]">
<button class="!cursor-default">close</button>
</form>
</dialog>

View File

@ -0,0 +1,26 @@
<script lang="ts">
import Icon from '@iconify/svelte';
import { addBlockedUser, fetchBlockedUsers } from '@app/admin/usersBlocked/actions.ts';
import { blockedUserCreateOrEditPopupState } from '@app/admin/usersBlocked/state.ts';
// lifecycle
$effect(() => {
fetchBlockedUsers();
});
// callbacks
async function onNewUserButtonClick() {
$blockedUserCreateOrEditPopupState = {
create: {
onUpdate: addBlockedUser
}
};
}
</script>
<div>
<button class="btn btn-soft w-full" onclick={() => onNewUserButtonClick()}>
<Icon icon="heroicons:plus-16-solid" />
<span>Neuer blockierter Nutzer</span>
</button>
</div>

View File

@ -0,0 +1,48 @@
<script lang="ts">
import Icon from '@iconify/svelte';
import CreateOrEditPopup from './CreateOrEditPopup.svelte';
import SortableTr from '@components/admin/table/SortableTr.svelte';
import SortableTh from '@components/admin/table/SortableTh.svelte';
import type { BlockedUser } from '@app/admin/usersBlocked/types.ts';
import { blockedUserCreateOrEditPopupState, blockedUsers } from '@app/admin/usersBlocked/state.ts';
import { editBlockedUser } from '@app/admin/usersBlocked/actions.ts';
// callbacks
async function onBlockedUserEditButtonClick(blockedUser: BlockedUser) {
$blockedUserCreateOrEditPopupState = {
edit: {
blockedUser: blockedUser,
onUpdate: editBlockedUser
}
};
}
</script>
<div class="h-screen overflow-x-auto">
<table class="table table-pin-rows">
<thead>
<SortableTr data={blockedUsers}>
<SortableTh style="width: 5%">#</SortableTh>
<SortableTh style="width: 20%" key="uuid">UUID</SortableTh>
<SortableTh style="width: 70%">Kommentar</SortableTh>
<SortableTh style="width: 5%"></SortableTh>
</SortableTr>
</thead>
<tbody>
{#each $blockedUsers as blockedUser, i (blockedUser)}
<tr class="hover:bg-base-200">
<td>{i + 1}</td>
<td>{blockedUser.uuid}</td>
<td>{blockedUser.comment}</td>
<td>
<button class="cursor-pointer" onclick={() => onBlockedUserEditButtonClick(blockedUser)}>
<Icon icon="heroicons:pencil-square" />
</button>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<CreateOrEditPopup />

View File

@ -0,0 +1,41 @@
import { actions } from 'astro:actions';
import { actionErrorPopup } from '@util/action.ts';
import { blockedUsers } from '@app/admin/usersBlocked/state.ts';
import type { BlockedUser } from '@app/admin/usersBlocked/types.ts';
export async function fetchBlockedUsers() {
const { data, error } = await actions.user.blocked();
if (error) {
actionErrorPopup(error);
return;
}
blockedUsers.set(data.blocked);
}
export async function addBlockedUser(blockedUser: BlockedUser) {
const { data, error } = await actions.user.addBlocked(blockedUser);
if (error) {
actionErrorPopup(error);
return;
}
blockedUsers.update((old) => {
old.push(Object.assign(blockedUser, { id: data.id }));
return old;
});
}
export async function editBlockedUser(blockedUser: BlockedUser) {
const { data, error } = await actions.user.editBlocked(blockedUser);
if (error) {
actionErrorPopup(error);
return;
}
blockedUsers.update((old) => {
const index = old.findIndex((a) => a.id == user.id);
old[index] = blockedUser;
return old;
});
}

View File

@ -0,0 +1,6 @@
import { writable } from 'svelte/store';
import type { BlockedUserCreateOrEditPopupState, BlockedUsers } from '@app/admin/usersBlocked/types.ts';
export const blockedUsers = writable<BlockedUsers>([]);
export const blockedUserCreateOrEditPopupState = writable<BlockedUserCreateOrEditPopupState>(null);

View File

@ -0,0 +1,9 @@
import { type ActionReturnType, actions } from 'astro:actions';
export type BlockedUsers = Exclude<ActionReturnType<typeof actions.user.blocked>['data'], undefined>['blocked'];
export type BlockedUser = BlockedUsers[0];
export type BlockedUserCreateOrEditPopupState =
| { create: { onUpdate: (blockedUser: BlockedUser) => void } }
| { edit: { blockedUser: BlockedUser; onUpdate: (blockedUser: BlockedUser) => void } }
| null;

View File

@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import Steve from '@assets/img/steve.png'; import Steve from '@assets/img/steve.png';
import Team from '@components/website/Team.svelte';
import type { GetDeathsRes } from '@db/schema/death.ts'; import type { GetDeathsRes } from '@db/schema/death.ts';
import { type ActionReturnType, actions } from 'astro:actions'; import { type ActionReturnType, actions } from 'astro:actions';
@ -26,7 +25,10 @@
{#each teams as team (team.id)} {#each teams as team (team.id)}
<tr> <tr>
<td> <td>
<Team name={team.name} color={team.color} /> <div class="flex items-center gap-x-2">
<div class="rounded-sm w-3 h-3" style="background-color: {team.color}"></div>
<h3 class="text-xs sm:text-xl">{team.name}</h3>
</div>
</td> </td>
<td class="max-w-9 overflow-ellipsis"> <td class="max-w-9 overflow-ellipsis">
{#if team.memberOne.id} {#if team.memberOne.id}

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { registeredPopupState } from '@components/website/signup/RegisteredPopup.ts'; import { registeredPopupState } from '@app/website/signup/RegisteredPopup.ts';
import Input from '@components/input/Input.svelte'; import Input from '@components/input/Input.svelte';
interface Props { interface Props {

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { teamPopupOpen, teamPopupName } from '@components/website/signup/TeamPopup.ts'; import { teamPopupOpen, teamPopupName } from '@app/website/signup/TeamPopup.ts';
let modal: HTMLDialogElement; let modal: HTMLDialogElement;
let form: HTMLFormElement; let form: HTMLFormElement;

View File

@ -1,13 +0,0 @@
<script lang="ts">
interface Props {
name: string;
color: string;
}
const { name, color }: Props = $props();
</script>
<div class="flex items-center gap-x-2">
<div class="rounded-sm w-3 h-3" style="background-color: {color}"></div>
<h3 class="text-xs sm:text-xl">{name}</h3>
</div>

View File

@ -28,6 +28,13 @@ CREATE TRIGGER IF NOT EXISTS user_username_update AFTER UPDATE ON user
END; END;
DELIMITER ; DELIMITER ;
-- blocked user
CREATE TABLE IF NOT EXISTS blocked_user (
id INT AUTO_INCREMENT PRIMARY KEY,
uuid VARCHAR(255) UNIQUE NOT NULL,
comment TINYTEXT
);
-- team -- team
CREATE TABLE IF NOT EXISTS team ( CREATE TABLE IF NOT EXISTS team (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
@ -40,8 +47,8 @@ CREATE TABLE IF NOT EXISTS team (
CREATE TABLE IF NOT EXISTS team_member ( CREATE TABLE IF NOT EXISTS team_member (
team_id INT NOT NULL, team_id INT NOT NULL,
user_id INT NOT NULL, user_id INT NOT NULL,
FOREIGN KEY (team_id) REFERENCES team(id), FOREIGN KEY (team_id) REFERENCES team(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES user(id) FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
); );
-- team draft -- team draft
@ -49,7 +56,7 @@ CREATE TABLE IF NOT EXISTS team_draft (
member_one_name VARCHAR(255) NOT NULL, member_one_name VARCHAR(255) NOT NULL,
member_two_name VARCHAR(255) NOT NULL, member_two_name VARCHAR(255) NOT NULL,
team_id INT NOT NULL, team_id INT NOT NULL,
FOREIGN KEY (team_id) REFERENCES team(id) FOREIGN KEY (team_id) REFERENCES team(id) ON DELETE CASCADE
); );
-- death -- death
@ -57,8 +64,8 @@ CREATE TABLE IF NOT EXISTS death (
message VARCHAR(1024) NOT NULL, message VARCHAR(1024) NOT NULL,
dead_user_id INT NOT NULL, dead_user_id INT NOT NULL,
killer_user_id INT, killer_user_id INT,
FOREIGN KEY (dead_user_id) REFERENCES user(id), FOREIGN KEY (dead_user_id) REFERENCES user(id) ON DELETE CASCADE,
FOREIGN KEY (killer_user_id) REFERENCES user(id) FOREIGN KEY (killer_user_id) REFERENCES user(id) ON DELETE CASCADE
); );
-- strike reason -- strike reason
@ -73,7 +80,7 @@ CREATE TABLE IF NOT EXISTS strike (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
at TIMESTAMP NOT NULL, at TIMESTAMP NOT NULL,
strike_reason_id INT NOT NULL, strike_reason_id INT NOT NULL,
FOREIGN KEY (strike_reason_id) REFERENCES strike_reason(id) FOREIGN KEY (strike_reason_id) REFERENCES strike_reason(id) ON DELETE CASCADE
); );
-- report -- report
@ -85,8 +92,8 @@ CREATE TABLE IF NOT EXISTS report (
created_at TIMESTAMP, created_at TIMESTAMP,
reporter_team_id INT NOT NULL, reporter_team_id INT NOT NULL,
reported_team_id INT, reported_team_id INT,
FOREIGN KEY (reporter_team_id) REFERENCES team(id), FOREIGN KEY (reporter_team_id) REFERENCES team(id) ON DELETE CASCADE,
FOREIGN KEY (reported_team_id) REFERENCES team(id) FOREIGN KEY (reported_team_id) REFERENCES team(id) ON DELETE CASCADE
); );
-- report status -- report status
@ -97,9 +104,9 @@ CREATE TABLE IF NOT EXISTS report_status (
report_id INT NOT NULL UNIQUE, report_id INT NOT NULL UNIQUE,
reviewer_id INT, reviewer_id INT,
strike_id INT, strike_id INT,
FOREIGN KEY (report_id) REFERENCES report(id), FOREIGN KEY (report_id) REFERENCES report(id) ON DELETE CASCADE,
FOREIGN KEY (reviewer_id) REFERENCES admin(id), FOREIGN KEY (reviewer_id) REFERENCES admin(id) ON DELETE CASCADE,
FOREIGN KEY (strike_id) REFERENCES strike(id) FOREIGN KEY (strike_id) REFERENCES strike(id) ON DELETE CASCADE
); );
-- feedback -- feedback
@ -111,7 +118,7 @@ CREATE TABLE IF NOT EXISTS feedback (
url_hash VARCHAR(255) NOT NULL UNIQUE, url_hash VARCHAR(255) NOT NULL UNIQUE,
last_changed TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, last_changed TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
user_id INT, user_id INT,
FOREIGN KEY (user_id) REFERENCES user(id) FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
); );
-- settings -- settings

View File

@ -105,6 +105,17 @@ import {
type GetReportStatusReq, type GetReportStatusReq,
reportStatus reportStatus
} from '@db/schema/reportStatus.ts'; } from '@db/schema/reportStatus.ts';
import {
addBlockedUser,
type AddBlockedUserReq,
getBlockedUsers,
type GetBlockedUsersReq,
blockedUser,
type GetBlockedUserByUuidReq,
getBlockedUserByUuid,
type EditBlockedUserReq,
editBlockedUser
} from '@db/schema/blockedUser.ts';
export class Database { export class Database {
protected readonly db: MySql2Database<{ protected readonly db: MySql2Database<{
@ -113,6 +124,7 @@ export class Database {
teamDraft: typeof teamDraft; teamDraft: typeof teamDraft;
teamMember: typeof teamMember; teamMember: typeof teamMember;
user: typeof user; user: typeof user;
blockedUser: typeof blockedUser;
death: typeof death; death: typeof death;
report: typeof report; report: typeof report;
reportStatus: typeof reportStatus; reportStatus: typeof reportStatus;
@ -139,6 +151,7 @@ export class Database {
teamDraft, teamDraft,
teamMember, teamMember,
user, user,
blockedUser,
death, death,
report, report,
reportStatus, reportStatus,
@ -173,6 +186,12 @@ export class Database {
getUserByUsername = (values: GetUserByUsernameReq) => getUserByUsername(this.db, values); getUserByUsername = (values: GetUserByUsernameReq) => getUserByUsername(this.db, values);
getUsersByUuid = (values: GetUsersByUuidReq) => getUsersByUuid(this.db, values); getUsersByUuid = (values: GetUsersByUuidReq) => getUsersByUuid(this.db, values);
/* user blocks */
addBlockedUser = (values: AddBlockedUserReq) => addBlockedUser(this.db, values);
editBlockedUser = (values: EditBlockedUserReq) => editBlockedUser(this.db, values);
getBlockedUserByUuid = (values: GetBlockedUserByUuidReq) => getBlockedUserByUuid(this.db, values);
getBlockedUsers = (values: GetBlockedUsersReq) => getBlockedUsers(this.db, values);
/* team */ /* team */
addTeam = (values: AddTeamReq) => addTeam(this.db, values); addTeam = (values: AddTeamReq) => addTeam(this.db, values);
editTeam = (values: EditTeamReq) => editTeam(this.db, values); editTeam = (values: EditTeamReq) => editTeam(this.db, values);

View File

@ -0,0 +1,50 @@
import { int, mysqlTable, varchar } from 'drizzle-orm/mysql-core';
import type { MySql2Database } from 'drizzle-orm/mysql2';
import { eq } from 'drizzle-orm';
type Database = MySql2Database<{ blockedUser: typeof blockedUser }>;
export const blockedUser = mysqlTable('blocked_user', {
id: int('id').primaryKey().autoincrement(),
uuid: varchar('uuid', { length: 255 }).unique().notNull(),
comment: varchar('comment', { length: 255 })
});
export type AddBlockedUserReq = {
uuid: string;
comment: string | null;
};
export type EditBlockedUserReq = {
id: number;
uuid: string;
comment: string | null;
};
export type GetBlockedUserByUuidReq = {
uuid: string;
};
export type GetBlockedUsersReq = {};
export async function addBlockedUser(db: Database, values: AddBlockedUserReq) {
const bu = await db.insert(blockedUser).values(values).$returningId();
return bu[0];
}
export async function editBlockedUser(db: Database, values: EditBlockedUserReq) {
await db.update(blockedUser).set(values).where(eq(blockedUser.id, values.id));
}
export async function getBlockedUserByUuid(db: Database, values: GetBlockedUserByUuidReq) {
const bu = await db.query.blockedUser.findFirst({
where: eq(blockedUser.uuid, values.uuid)
});
return bu ?? null;
}
export async function getBlockedUsers(db: Database, _values: GetBlockedUsersReq) {
return db.select().from(blockedUser);
}

View File

@ -27,6 +27,13 @@ const adminTabs = [
href: 'admin/users', href: 'admin/users',
name: 'Registrierte Nutzer', name: 'Registrierte Nutzer',
icon: 'heroicons:user', icon: 'heroicons:user',
subTabs: [
{
href: 'admin/users/blocked',
name: 'Blockierte Nutzer',
icon: 'heroicons:user-minus'
}
],
enabled: session?.permissions.users enabled: session?.permissions.users
}, },
{ {
@ -65,7 +72,7 @@ const adminTabs = [
<BaseLayout title={title}> <BaseLayout title={title}>
<ClientRouter /> <ClientRouter />
<div class="flex flex-row max-h-[100vh]"> <div class="flex flex-row max-h-[100vh]">
<ul class="menu bg-base-200 w-64 h-[100vh] flex"> <ul class="menu bg-base-200 w-68 h-[100vh] flex">
{ {
preTabs.map((tab) => ( preTabs.map((tab) => (
<li> <li>
@ -84,6 +91,18 @@ const adminTabs = [
<Icon name={tab.icon} /> <Icon name={tab.icon} />
<span>{tab.name}</span> <span>{tab.name}</span>
</a> </a>
{tab.subTabs && (
<ul>
{tab.subTabs.map((subTab) => (
<li>
<a href={subTab.href}>
<Icon name={subTab.icon} />
<span>{subTab.name}</span>
</a>
</li>
))}
</ul>
)}
</li> </li>
)) ))
} }

View File

@ -2,7 +2,7 @@
import '@assets/website_layout.css'; import '@assets/website_layout.css';
import BaseLayout from '../BaseLayout.astro'; import BaseLayout from '../BaseLayout.astro';
import { Icon } from 'astro-icon/components'; import { Icon } from 'astro-icon/components';
import Menu from '@components/website/layout/Menu.svelte'; import Menu from '@app/layout/Menu.svelte';
interface Props { interface Props {
title: string; title: string;

View File

@ -0,0 +1,16 @@
---
import AdminLayout from '@layouts/admin/AdminLayout.astro';
import UsersBlocked from '@app/admin/usersBlocked/UsersBlocked.svelte';
import SidebarActions from '@app/admin/usersBlocked/SidebarActions.svelte';
import { Session } from '@util/session.ts';
import { Permissions } from '@util/permissions.ts';
import { BASE_PATH } from 'astro:env/server';
const session = Session.sessionFromCookies(Astro.cookies, Permissions.Admin);
if (!session) return Astro.redirect(`${BASE_PATH}/admin`);
---
<AdminLayout title="Blockierte Nutzer">
<SidebarActions slot="actions" client:load />
<UsersBlocked client:load />
</AdminLayout>

View File

@ -1,8 +1,8 @@
--- ---
import WebsiteLayout from '@layouts/website/WebsiteLayout.astro'; import WebsiteLayout from '@layouts/website/WebsiteLayout.astro';
import Scroll from '@components/website/index/Scroll.svelte'; import Scroll from '@app/website/index/Scroll.svelte';
import Teams from '@app/webite/index/Teams.svelte'; import Teams from '@app/website/index/Teams.svelte';
import Countdown from '@components/website/index/Countdown.svelte'; import Countdown from '@app/website/index/Countdown.svelte';
import Varo from '@assets/img/varo.webp'; import Varo from '@assets/img/varo.webp';
import Background from '@assets/img/background.webp'; import Background from '@assets/img/background.webp';
import { START_DATE } from 'astro:env/server'; import { START_DATE } from 'astro:env/server';

View File

@ -2,10 +2,10 @@
import WebsiteLayout from '@layouts/website/WebsiteLayout.astro'; import WebsiteLayout from '@layouts/website/WebsiteLayout.astro';
import Checkbox from '@components/input/Checkbox.svelte'; import Checkbox from '@components/input/Checkbox.svelte';
import Input from '@components/input/Input.svelte'; import Input from '@components/input/Input.svelte';
import RulesPopup from '@components/website/signup/RulesPopup.svelte'; import RulesPopup from '@app/website/signup/RulesPopup.svelte';
import Popup from '@components/popup/Popup.svelte'; import Popup from '@components/popup/Popup.svelte';
import TeamPopup from '@components/website/signup/TeamPopup.svelte'; import TeamPopup from '@app/website/signup/TeamPopup.svelte';
import RegisteredPopup from '@components/website/signup/RegisteredPopup.svelte'; import RegisteredPopup from '@app/website/signup/RegisteredPopup.svelte';
import { getSettings, SettingKey } from '@util/settings'; import { getSettings, SettingKey } from '@util/settings';
import { db } from '@db/database.ts'; import { db } from '@db/database.ts';
import { DISCORD_LINK, PAYPAL_LINK, START_DATE, TEAMSPEAK_LINK } from 'astro:env/server'; import { DISCORD_LINK, PAYPAL_LINK, START_DATE, TEAMSPEAK_LINK } from 'astro:env/server';
@ -151,9 +151,9 @@ const signupDisabledSubMessage = signupSetting[SettingKey.SignupDisabledSubMessa
<script> <script>
import { actions } from 'astro:actions'; import { actions } from 'astro:actions';
import { popupState } from '@components/popup/Popup'; import { popupState } from '@components/popup/Popup';
import { rulesPopupState, rulesPopupRead } from '@components/website/signup/RulesPopup'; import { rulesPopupState, rulesPopupRead } from '@app/website/signup/RulesPopup';
import { teamPopupName, teamPopupOpen } from '@components/website/signup/TeamPopup'; import { teamPopupName, teamPopupOpen } from '@app/website/signup/TeamPopup';
import { registeredPopupState } from '@components/website/signup/RegisteredPopup'; import { registeredPopupState } from '@app/website/signup/RegisteredPopup';
/* ----- client validation ----- */ /* ----- client validation ----- */
const rulesCheckbox = document.getElementById('rules')! as HTMLInputElement; const rulesCheckbox = document.getElementById('rules')! as HTMLInputElement;