add feedback blank filter option

This commit is contained in:
2026-01-03 19:23:30 +01:00
parent dd063c568d
commit a197c002f4
7 changed files with 36 additions and 15 deletions

View File

@@ -38,11 +38,14 @@ export const feedback = {
} }
}), }),
feedbacks: defineAction({ feedbacks: defineAction({
handler: async (_, context) => { input: z.object({
includeBlanks: z.boolean()
}),
handler: async (input, context) => {
Session.actionSessionFromCookies(context.cookies, Permissions.Feedback); Session.actionSessionFromCookies(context.cookies, Permissions.Feedback);
return { return {
feedbacks: await db.getFeedbacks({}) feedbacks: await db.getFeedbacks({ includeBlanks: input.includeBlanks })
}; };
} }
}) })

View File

@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import BottomBar from './BottomBar.svelte'; import BottomBar from './BottomBar.svelte';
import { feedbacks, fetchFeedbacks, type Feedback } from '@app/admin/feedback/feedback.ts'; import { feedbacks, type Feedback } from '@app/admin/feedback/feedback.ts';
import { onMount } from 'svelte';
import DataTable from '@components/admin/table/DataTable.svelte'; import DataTable from '@components/admin/table/DataTable.svelte';
// consts // consts
@@ -15,11 +14,6 @@
// states // states
let activeFeedback = $state<Feedback | null>(null); let activeFeedback = $state<Feedback | null>(null);
// lifecycle
onMount(() => {
fetchFeedbacks();
});
</script> </script>
{#snippet date(value: string)} {#snippet date(value: string)}

View File

@@ -0,0 +1,19 @@
<script lang="ts">
import Checkbox from '@components/input/Checkbox.svelte';
import { fetchFeedbacks } from './feedback';
// states
let showBlank = $state(false);
// lifecycle
$effect(() => {
fetchFeedbacks(showBlank);
});
</script>
<div>
<fieldset class="fieldset border border-base-content/50 rounded-box p-2">
<legend class="fieldset-legend">Filter</legend>
<Checkbox bind:checked={showBlank} label="Unausgefüllte zeigen" />
</fieldset>
</div>

View File

@@ -10,8 +10,8 @@ export type Feedback = Feedbacks[0];
export const feedbacks = writable<Feedbacks>([]); export const feedbacks = writable<Feedbacks>([]);
// actions // actions
export async function fetchFeedbacks(reporter?: string | null, reported?: string | null) { export async function fetchFeedbacks(includeBlanks: boolean) {
const { data, error } = await actions.feedback.feedbacks({ reporter: reporter, reported: reported }); const { data, error } = await actions.feedback.feedbacks({ includeBlanks: includeBlanks });
if (error) { if (error) {
actionErrorPopup(error); actionErrorPopup(error);
return; return;

View File

@@ -1,7 +1,7 @@
import { int, mysqlTable, text, timestamp, varchar } from 'drizzle-orm/mysql-core'; import { int, mysqlTable, text, timestamp, varchar } from 'drizzle-orm/mysql-core';
import { user } from './user.ts'; import { user } from './user.ts';
import type { MySql2Database } from 'drizzle-orm/mysql2'; import type { MySql2Database } from 'drizzle-orm/mysql2';
import { desc, eq, inArray } from 'drizzle-orm'; import { desc, eq, inArray, isNotNull } from 'drizzle-orm';
import { generateRandomString } from '@util/random.ts'; import { generateRandomString } from '@util/random.ts';
type Database = MySql2Database<{ feedback: typeof feedback }>; type Database = MySql2Database<{ feedback: typeof feedback }>;
@@ -32,7 +32,9 @@ export type SubmitFeedbackReq = {
content: string; content: string;
}; };
export type GetFeedbacksReq = {}; export type GetFeedbacksReq = {
includeBlanks?: boolean | null;
};
export type GetFeedbackByUrlHash = { export type GetFeedbackByUrlHash = {
urlHash: string; urlHash: string;
@@ -76,7 +78,7 @@ export async function submitFeedback(db: Database, values: SubmitFeedbackReq) {
.where(eq(feedback.urlHash, values.urlHash)); .where(eq(feedback.urlHash, values.urlHash));
} }
export async function getFeedbacks(db: Database, _values: GetFeedbacksReq) { export async function getFeedbacks(db: Database, values: GetFeedbacksReq) {
return db return db
.select({ .select({
id: feedback.id, id: feedback.id,
@@ -89,6 +91,7 @@ export async function getFeedbacks(db: Database, _values: GetFeedbacksReq) {
}) })
.from(feedback) .from(feedback)
.leftJoin(user, eq(feedback.userId, user.id)) .leftJoin(user, eq(feedback.userId, user.id))
.where(!values.includeBlanks ? isNotNull(feedback.lastChanged) : undefined)
.orderBy(desc(feedback.id)); .orderBy(desc(feedback.id));
} }

View File

@@ -133,7 +133,7 @@ export async function getReports(db: Database, values: GetReportsReq) {
and( and(
values.reporter != null ? eq(reporter.username, values.reporter) : undefined, values.reporter != null ? eq(reporter.username, values.reporter) : undefined,
values.reported != null ? eq(reported.username, values.reported) : undefined, values.reported != null ? eq(reported.username, values.reported) : undefined,
values.includeDrafts == false ? isNotNull(report.createdAt) : undefined !values.includeDrafts ? isNotNull(report.createdAt) : undefined
) )
) )
.orderBy(desc(report.id)); .orderBy(desc(report.id));

View File

@@ -4,11 +4,13 @@ import AdminLayout from '@layouts/admin/AdminLayout.astro';
import { Session } from '@util/session.ts'; import { Session } from '@util/session.ts';
import { Permissions } from '@util/permissions.ts'; import { Permissions } from '@util/permissions.ts';
import { BASE_PATH } from 'astro:env/server'; import { BASE_PATH } from 'astro:env/server';
import SidebarActions from '@app/admin/feedback/SidebarActions.svelte';
const session = Session.sessionFromCookies(Astro.cookies, Permissions.Feedback); const session = Session.sessionFromCookies(Astro.cookies, Permissions.Feedback);
if (!session) return Astro.redirect(`${BASE_PATH}/admin`); if (!session) return Astro.redirect(`${BASE_PATH}/admin`);
--- ---
<AdminLayout title="Feedback"> <AdminLayout title="Feedback">
<SidebarActions slot="actions" client:load />
<Feedback client:load /> <Feedback client:load />
</AdminLayout> </AdminLayout>