add delete to admin panel
All checks were successful
deploy / build-and-deploy (push) Successful in 21s
All checks were successful
deploy / build-and-deploy (push) Successful in 21s
This commit is contained in:
parent
e47268111a
commit
45f984e4da
@ -33,6 +33,16 @@ export const admin = {
|
||||
await db.editAdmin(input);
|
||||
}
|
||||
}),
|
||||
deleteAdmin: defineAction({
|
||||
input: z.object({
|
||||
id: z.number()
|
||||
}),
|
||||
handler: async (input, context) => {
|
||||
Session.actionSessionFromCookies(context.cookies, Permissions.Admin);
|
||||
|
||||
await db.deleteAdmin(input);
|
||||
}
|
||||
}),
|
||||
admins: defineAction({
|
||||
handler: async (_, context) => {
|
||||
Session.actionSessionFromCookies(context.cookies, Permissions.Admin);
|
||||
|
@ -105,6 +105,16 @@ export const team = {
|
||||
});
|
||||
}
|
||||
}),
|
||||
deleteTeam: defineAction({
|
||||
input: z.object({
|
||||
id: z.number()
|
||||
}),
|
||||
handler: async (input, context) => {
|
||||
Session.actionSessionFromCookies(context.cookies, Permissions.Users);
|
||||
|
||||
await db.deleteTeam(input);
|
||||
}
|
||||
}),
|
||||
teams: defineAction({
|
||||
input: z.object({
|
||||
name: z.string().nullish(),
|
||||
|
@ -70,6 +70,16 @@ export const user = {
|
||||
});
|
||||
}
|
||||
}),
|
||||
deleteUser: defineAction({
|
||||
input: z.object({
|
||||
id: z.number()
|
||||
}),
|
||||
handler: async (input, context) => {
|
||||
Session.actionSessionFromCookies(context.cookies, Permissions.Users);
|
||||
|
||||
await db.deleteUser(input);
|
||||
}
|
||||
}),
|
||||
users: defineAction({
|
||||
input: z.object({
|
||||
username: z.string().nullish(),
|
||||
@ -112,6 +122,16 @@ export const user = {
|
||||
await db.editBlockedUser(input);
|
||||
}
|
||||
}),
|
||||
deleteBlocked: defineAction({
|
||||
input: z.object({
|
||||
id: z.number()
|
||||
}),
|
||||
handler: async (input, context) => {
|
||||
Session.actionSessionFromCookies(context.cookies, Permissions.Users);
|
||||
|
||||
await db.deleteBlockedUser(input);
|
||||
}
|
||||
}),
|
||||
blocked: defineAction({
|
||||
handler: async (_, context) => {
|
||||
Session.actionSessionFromCookies(context.cookies, Permissions.Users);
|
||||
|
@ -1,13 +1,23 @@
|
||||
<script lang="ts">
|
||||
import BitBadge from '@components/input/BitBadge.svelte';
|
||||
import { Permissions } from '@util/permissions.ts';
|
||||
import { admins, editAdmin } from '@app/admin/admins/admins.ts';
|
||||
import { type Admin, admins, deleteAdmin, editAdmin } from '@app/admin/admins/admins.ts';
|
||||
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
|
||||
import DataTable from '@components/admin/table/DataTable.svelte';
|
||||
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
|
||||
|
||||
// state
|
||||
let editPopupAdmin = $state(null);
|
||||
let editPopupOpen = $derived(!!editPopupAdmin);
|
||||
|
||||
// callback
|
||||
function onAdminDelete(admin: Admin) {
|
||||
$confirmPopupState = {
|
||||
title: 'Admin löschen',
|
||||
message: 'Soll der Admin wirklich gelöscht werden?',
|
||||
onConfirm: () => deleteAdmin(admin)
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#snippet permissionsBadge(permissions: number)}
|
||||
@ -22,6 +32,7 @@
|
||||
{ key: 'permissions', label: 'Berechtigungen', width: 60, transform: permissionsBadge }
|
||||
]}
|
||||
onEdit={(admin) => (editPopupAdmin = admin)}
|
||||
onDelete={onAdminDelete}
|
||||
/>
|
||||
|
||||
<CrudPopup
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { type ActionReturnType, actions } from 'astro:actions';
|
||||
import { writable } from 'svelte/store';
|
||||
import { actionErrorPopup } from '@util/action.ts';
|
||||
import { addToWritableArray, deleteFromWritableArray, updateWritableArray } from '@util/state.ts';
|
||||
|
||||
// types
|
||||
export type Admins = Exclude<ActionReturnType<typeof actions.admin.admins>['data'], undefined>['admins'];
|
||||
@ -27,10 +28,7 @@ export async function addAdmin(admin: Admin & { password: string }) {
|
||||
return;
|
||||
}
|
||||
|
||||
admins.update((old) => {
|
||||
old.push(Object.assign(admin, { id: data.id }));
|
||||
return old;
|
||||
});
|
||||
addToWritableArray(admins, Object.assign(admin, { id: data.id }));
|
||||
}
|
||||
|
||||
export async function editAdmin(admin: Admin & { password: string }) {
|
||||
@ -40,9 +38,15 @@ export async function editAdmin(admin: Admin & { password: string }) {
|
||||
return;
|
||||
}
|
||||
|
||||
admins.update((old) => {
|
||||
const index = old.findIndex((a) => a.id == admin.id);
|
||||
old[index] = admin;
|
||||
return old;
|
||||
});
|
||||
updateWritableArray(admins, admin, (t) => t.id == admin.id);
|
||||
}
|
||||
|
||||
export async function deleteAdmin(admin: Admin) {
|
||||
const { error } = await actions.admin.deleteAdmin(admin);
|
||||
if (error) {
|
||||
actionErrorPopup(error);
|
||||
return;
|
||||
}
|
||||
|
||||
deleteFromWritableArray(admins, (t) => t.id == admin.id);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { addTeam, teams } from '@app/admin/teams/teams.ts';
|
||||
import { addTeam, deleteTeam, type Team, teams } from '@app/admin/teams/teams.ts';
|
||||
import DataTable from '@components/admin/table/DataTable.svelte';
|
||||
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
|
||||
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
|
||||
|
||||
// state
|
||||
let editPopupTeam = $state(null);
|
||||
@ -11,6 +12,15 @@
|
||||
$effect(() => {
|
||||
if (!editPopupOpen) editPopupTeam = null;
|
||||
});
|
||||
|
||||
// callback
|
||||
function onTeamDelete(team: Team) {
|
||||
$confirmPopupState = {
|
||||
title: 'Team löschen?',
|
||||
message: 'Soll das Team wirklich gelöscht werden?',
|
||||
onConfirm: () => deleteTeam(team)
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#snippet color(value: string)}
|
||||
@ -27,6 +37,7 @@
|
||||
{ key: 'memberTwo.username', label: 'Spieler 2', width: 30 }
|
||||
]}
|
||||
onEdit={(team) => (editPopupTeam = team)}
|
||||
onDelete={onTeamDelete}
|
||||
/>
|
||||
|
||||
<CrudPopup
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { type ActionReturnType, actions } from 'astro:actions';
|
||||
import { writable } from 'svelte/store';
|
||||
import { actionErrorPopup } from '@util/action.ts';
|
||||
import { addToWritableArray, deleteFromWritableArray, updateWritableArray } from '@util/state.ts';
|
||||
|
||||
// types
|
||||
export type Teams = Exclude<ActionReturnType<typeof actions.team.teams>['data'], undefined>['teams'];
|
||||
export type Team = Teams[0];
|
||||
|
||||
export type Users = Exclude<ActionReturnType<typeof actions.user.users>['data'], undefined>['users'];
|
||||
|
||||
// state
|
||||
export const teams = writable<Teams>([]);
|
||||
|
||||
@ -29,10 +28,7 @@ export async function addTeam(team: Team) {
|
||||
return;
|
||||
}
|
||||
|
||||
teams.update((old) => {
|
||||
old.push(Object.assign(team, { id: data.id }));
|
||||
return old;
|
||||
});
|
||||
addToWritableArray(teams, Object.assign(team, { id: data.id }));
|
||||
}
|
||||
|
||||
export async function editTeam(team: Team) {
|
||||
@ -42,9 +38,15 @@ export async function editTeam(team: Team) {
|
||||
return;
|
||||
}
|
||||
|
||||
teams.update((old) => {
|
||||
const index = old.findIndex((a) => a.id == team.id);
|
||||
old[index] = team;
|
||||
return old;
|
||||
});
|
||||
updateWritableArray(teams, team, (t) => t.id == team.id);
|
||||
}
|
||||
|
||||
export async function deleteTeam(team: Team) {
|
||||
const { error } = await actions.team.deleteTeam(team);
|
||||
if (error) {
|
||||
actionErrorPopup(error);
|
||||
return;
|
||||
}
|
||||
|
||||
deleteFromWritableArray(teams, (t) => t.id == team.id);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import DataTable from '@components/admin/table/DataTable.svelte';
|
||||
import { editUser, users } from '@app/admin/users/users.ts';
|
||||
import { deleteUser, editUser, type User, users } from '@app/admin/users/users.ts';
|
||||
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
|
||||
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
|
||||
|
||||
// state
|
||||
let editPopupUser = $state(null);
|
||||
@ -11,6 +12,15 @@
|
||||
$effect(() => {
|
||||
if (!editPopupOpen) editPopupUser = null;
|
||||
});
|
||||
|
||||
// callback
|
||||
function onUserDelete(user: User) {
|
||||
$confirmPopupState = {
|
||||
title: 'Nutzer löschen?',
|
||||
message: 'Soll der Nutzer wirklich gelöscht werden?',
|
||||
onConfirm: () => deleteUser(user)
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<DataTable
|
||||
@ -25,6 +35,7 @@
|
||||
{ key: 'uuid', label: 'UUID', width: 23 }
|
||||
]}
|
||||
onEdit={(user) => (editPopupUser = user)}
|
||||
onDelete={onUserDelete}
|
||||
/>
|
||||
|
||||
<CrudPopup
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import { type ActionReturnType, actions } from 'astro:actions';
|
||||
import { actionErrorPopup } from '@util/action.ts';
|
||||
import { addToWritableArray, deleteFromWritableArray, updateWritableArray } from '@util/state.ts';
|
||||
|
||||
// types
|
||||
export type Users = Exclude<ActionReturnType<typeof actions.user.users>['data'], undefined>['users'];
|
||||
@ -34,10 +35,7 @@ export async function addUser(user: User) {
|
||||
return;
|
||||
}
|
||||
|
||||
users.update((old) => {
|
||||
old.push(Object.assign(user, { id: data.id }));
|
||||
return old;
|
||||
});
|
||||
addToWritableArray(users, user);
|
||||
}
|
||||
|
||||
export async function editUser(user: User) {
|
||||
@ -55,9 +53,15 @@ export async function editUser(user: User) {
|
||||
return;
|
||||
}
|
||||
|
||||
users.update((old) => {
|
||||
const index = old.findIndex((a) => a.id == user.id);
|
||||
old[index] = user;
|
||||
return old;
|
||||
});
|
||||
updateWritableArray(users, user, (t) => t.id == user.id);
|
||||
}
|
||||
|
||||
export async function deleteUser(user: User) {
|
||||
const { error } = await actions.user.deleteUser({ id: user.id });
|
||||
if (error) {
|
||||
actionErrorPopup(error);
|
||||
return;
|
||||
}
|
||||
|
||||
deleteFromWritableArray(users, (t) => t.id == user.id);
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { blockedUsers, editBlockedUser } from '@app/admin/usersBlocked/usersBlocked.ts';
|
||||
import {
|
||||
type BlockedUser,
|
||||
blockedUsers,
|
||||
deleteBlockedUser,
|
||||
editBlockedUser
|
||||
} from '@app/admin/usersBlocked/usersBlocked.ts';
|
||||
import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
|
||||
import DataTable from '@components/admin/table/DataTable.svelte';
|
||||
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
|
||||
|
||||
// state
|
||||
let blockedUserEditPopupBlockedUser = $state(null);
|
||||
@ -11,6 +17,15 @@
|
||||
$effect(() => {
|
||||
if (!blockedUserEditPopupOpen) blockedUserEditPopupBlockedUser = null;
|
||||
});
|
||||
|
||||
// callback
|
||||
function onBlockedUserDelete(blockedUser: BlockedUser) {
|
||||
$confirmPopupState = {
|
||||
title: 'Nutzer entblockieren?',
|
||||
message: 'Soll der Nutzer wirklich entblockiert werden?\nDieser kann sich danach wieder registrieren.',
|
||||
onConfirm: () => deleteBlockedUser(blockedUser)
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<DataTable
|
||||
@ -21,6 +36,7 @@
|
||||
{ key: 'comment', label: 'Kommentar', width: 70 }
|
||||
]}
|
||||
onEdit={(blockedUser) => (blockedUserEditPopupBlockedUser = blockedUser)}
|
||||
onDelete={onBlockedUserDelete}
|
||||
/>
|
||||
|
||||
<CrudPopup
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { type ActionReturnType, actions } from 'astro:actions';
|
||||
import { writable } from 'svelte/store';
|
||||
import { actionErrorPopup } from '@util/action.ts';
|
||||
import { addToWritableArray, deleteFromWritableArray, updateWritableArray } from '@util/state.ts';
|
||||
|
||||
// types
|
||||
export type BlockedUsers = Exclude<ActionReturnType<typeof actions.user.blocked>['data'], undefined>['blocked'];
|
||||
@ -27,10 +28,7 @@ export async function addBlockedUser(blockedUser: BlockedUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
blockedUsers.update((old) => {
|
||||
old.push(Object.assign(blockedUser, { id: data.id }));
|
||||
return old;
|
||||
});
|
||||
addToWritableArray(blockedUsers, Object.assign(blockedUser, { id: data.id }));
|
||||
}
|
||||
|
||||
export async function editBlockedUser(blockedUser: BlockedUser) {
|
||||
@ -40,9 +38,15 @@ export async function editBlockedUser(blockedUser: BlockedUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
blockedUsers.update((old) => {
|
||||
const index = old.findIndex((a) => a.id == blockedUser.id);
|
||||
old[index] = blockedUser;
|
||||
return old;
|
||||
});
|
||||
updateWritableArray(blockedUsers, blockedUser, (t) => t.id == blockedUser.id);
|
||||
}
|
||||
|
||||
export async function deleteBlockedUser(blockedUser: BlockedUser) {
|
||||
const { error } = await actions.user.deleteBlocked(blockedUser);
|
||||
if (error) {
|
||||
actionErrorPopup(error);
|
||||
return;
|
||||
}
|
||||
|
||||
deleteFromWritableArray(blockedUsers, (t) => t.id == blockedUser.id);
|
||||
}
|
||||
|
@ -21,10 +21,11 @@
|
||||
|
||||
onClick?: (t: T) => void;
|
||||
onEdit?: (t: T) => void;
|
||||
onDelete?: (t: T) => void;
|
||||
}
|
||||
|
||||
// input
|
||||
let { data, count, keys, onClick, onEdit }: Props<any> = $props();
|
||||
let { data, count, keys, onClick, onEdit, onDelete }: Props<any> = $props();
|
||||
</script>
|
||||
|
||||
<div class="h-screen overflow-x-auto">
|
||||
@ -39,7 +40,7 @@
|
||||
>{key.label}</SortableTh
|
||||
>
|
||||
{/each}
|
||||
{#if onEdit}
|
||||
{#if onEdit || onDelete}
|
||||
<SortableTh style="width: 5%"></SortableTh>
|
||||
{/if}
|
||||
</SortableTr>
|
||||
@ -59,11 +60,18 @@
|
||||
{/if}
|
||||
</td>
|
||||
{/each}
|
||||
{#if onEdit}
|
||||
{#if onEdit || onDelete}
|
||||
<td>
|
||||
<button class="cursor-pointer" onclick={() => onEdit(d)}>
|
||||
<Icon icon="heroicons:pencil-square" />
|
||||
</button>
|
||||
{#if onEdit}
|
||||
<button class="cursor-pointer" onclick={() => onEdit(d)}>
|
||||
<Icon icon="heroicons:pencil-square" />
|
||||
</button>
|
||||
{/if}
|
||||
{#if onDelete}
|
||||
<button class="cursor-pointer" onclick={() => onDelete(d)}>
|
||||
<Icon icon="heroicons:trash" />
|
||||
</button>
|
||||
{/if}
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
|
@ -9,7 +9,9 @@ import {
|
||||
type AddAdminReq,
|
||||
addAdmin,
|
||||
type EditAdminReq,
|
||||
editAdmin
|
||||
editAdmin,
|
||||
type DeleteAdminReq,
|
||||
deleteAdmin
|
||||
} from './schema/admin';
|
||||
import {
|
||||
addTeam,
|
||||
@ -114,7 +116,9 @@ import {
|
||||
type GetBlockedUserByUuidReq,
|
||||
getBlockedUserByUuid,
|
||||
type EditBlockedUserReq,
|
||||
editBlockedUser
|
||||
editBlockedUser,
|
||||
type DeleteBlockedUserReq,
|
||||
deleteBlockedUser
|
||||
} from '@db/schema/blockedUser.ts';
|
||||
|
||||
export class Database {
|
||||
@ -173,6 +177,7 @@ export class Database {
|
||||
/* admins */
|
||||
addAdmin = (values: AddAdminReq) => addAdmin(this.db, values);
|
||||
editAdmin = (values: EditAdminReq) => editAdmin(this.db, values);
|
||||
deleteAdmin = (values: DeleteAdminReq) => deleteAdmin(this.db, values);
|
||||
getAdmins = (values: GetAdminReq) => getAdmins(this.db, values);
|
||||
existsAdmin = (values: ExistsAdminReq) => existsAdmin(this.db, values);
|
||||
|
||||
@ -189,6 +194,7 @@ export class Database {
|
||||
/* user blocks */
|
||||
addBlockedUser = (values: AddBlockedUserReq) => addBlockedUser(this.db, values);
|
||||
editBlockedUser = (values: EditBlockedUserReq) => editBlockedUser(this.db, values);
|
||||
deleteBlockedUser = (values: DeleteBlockedUserReq) => deleteBlockedUser(this.db, values);
|
||||
getBlockedUserByUuid = (values: GetBlockedUserByUuidReq) => getBlockedUserByUuid(this.db, values);
|
||||
getBlockedUsers = (values: GetBlockedUsersReq) => getBlockedUsers(this.db, values);
|
||||
|
||||
|
@ -22,6 +22,10 @@ export type EditAdminReq = {
|
||||
permissions: number;
|
||||
};
|
||||
|
||||
export type DeleteAdminReq = {
|
||||
id: number;
|
||||
};
|
||||
|
||||
export type GetAdminReq = {};
|
||||
export type GetAdminRes = Omit<typeof admin.$inferSelect, 'password'>[];
|
||||
|
||||
@ -55,6 +59,10 @@ export async function editAdmin(db: Database, values: EditAdminReq) {
|
||||
.where(eq(admin.id, values.id));
|
||||
}
|
||||
|
||||
export async function deleteAdmin(db: Database, values: DeleteAdminReq) {
|
||||
return db.delete(admin).where(eq(admin.id, values.id));
|
||||
}
|
||||
|
||||
export async function getAdmins(db: Database, _values: GetAdminReq): Promise<GetAdminRes> {
|
||||
return db.select({ id: admin.id, username: admin.username, permissions: admin.permissions }).from(admin);
|
||||
}
|
||||
|
@ -21,6 +21,10 @@ export type EditBlockedUserReq = {
|
||||
comment?: string | null;
|
||||
};
|
||||
|
||||
export type DeleteBlockedUserReq = {
|
||||
id: number;
|
||||
};
|
||||
|
||||
export type GetBlockedUserByUuidReq = {
|
||||
uuid: string;
|
||||
};
|
||||
@ -37,6 +41,10 @@ export async function editBlockedUser(db: Database, values: EditBlockedUserReq)
|
||||
await db.update(blockedUser).set(values).where(eq(blockedUser.id, values.id));
|
||||
}
|
||||
|
||||
export async function deleteBlockedUser(db: Database, values: DeleteBlockedUserReq) {
|
||||
return db.delete(blockedUser).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)
|
||||
|
24
src/util/state.ts
Normal file
24
src/util/state.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
export function addToWritableArray<T>(writable: Writable<T[]>, t: T) {
|
||||
writable.update((old) => {
|
||||
old.push(t);
|
||||
return old;
|
||||
});
|
||||
}
|
||||
|
||||
export function updateWritableArray<T>(writable: Writable<T[]>, t: T, cmp: (t: T) => boolean) {
|
||||
writable.update((old) => {
|
||||
const index = old.findIndex(cmp);
|
||||
old[index] = t;
|
||||
return old;
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteFromWritableArray<T>(writable: Writable<T[]>, cmp: (t: T) => boolean) {
|
||||
writable.update((old) => {
|
||||
const index = old.findIndex(cmp);
|
||||
old.splice(index, 1);
|
||||
return old;
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user