feat: Add admin experiment management pages and components

- Implemented ExperimentPage for viewing and editing individual experiments.
- Created ExperimentsAdminPage for managing all experiments with a list and a modal for creating new experiments.
- Developed AnnotatePage for annotating experiments with different views based on annotation tools.
- Added CreateExperimentModal for creating new experiments with dataset selection.
- Introduced DeleteExperimentButton for confirming and executing experiment deletions.
- Built EditableExperimentHeader for editing experiment details.
- Created ExperimentsList component to display a list of experiments with links to their details.
This commit is contained in:
averel10
2026-03-20 07:26:02 +01:00
parent 4b35e2392e
commit dd5d99ec24
16 changed files with 1593 additions and 60 deletions

View File

@@ -87,7 +87,7 @@ export async function saveAnnotations(
/** Returns all datasets (for the home page). */
export async function getAllExperiments() {
return db.select().from(experiment).orderBy(experiment.id);
return db.select().from(experiment).where(eq(experiment.published, true)).orderBy(experiment.id);
}
/** Returns annotation progress for the current user in a dataset. */

View File

@@ -7,6 +7,24 @@ import { revalidatePath } from 'next/cache';
import { removeAllDatasetEntries } from './remove-dataset-entries';
import { requireAdmin } from '@/lib/auth';
export async function getAllDatasets() {
const result = await requireAdmin();
if (!result.authenticated || !result.admin) {
throw new Error('Unauthorized');
}
try {
const datasets = await db
.select()
.from(dataset)
.orderBy(dataset.name);
return datasets;
} catch (error) {
console.error('Error fetching datasets:', error);
throw new Error('Failed to fetch datasets');
}
}
export async function createDataset({ name, description }: { name: string; description?: string }) {
const result = await requireAdmin();
if (!result.authenticated || !result.admin) {

View File

@@ -0,0 +1,146 @@
'use server';
import db from '@/lib/db';
import { experiment } from '@/lib/model/experiment';
import { and, eq } from 'drizzle-orm';
import { revalidatePath } from 'next/cache';
import { auth, requireAdmin } from '@/lib/auth';
import { headers } from 'next/headers';
export async function createExperiment({
name,
description,
datasetId,
annotationTool,
published = false
}: {
name: string;
description?: string;
datasetId: number;
annotationTool: string;
published?: boolean;
}) {
const result = await requireAdmin();
if (!result.authenticated || !result.admin) {
throw new Error('Unauthorized');
}
if (!name || !name.trim()) {
throw new Error('Experiment name is required');
}
try {
const result = await db.insert(experiment).values({
name: name.trim(),
description: description?.trim() || null,
datasetId,
annotationTool: annotationTool.trim(),
published,
});
revalidatePath('/admin/experiments');
return result;
} catch (error) {
console.error('Error creating experiment:', error);
throw new Error('Failed to create experiment');
}
}
export async function updateExperiment(
id: number,
updates: { name?: string; description?: string, annotationTool?: string, published?: boolean }
) {
const result = await requireAdmin();
if (!result.authenticated || !result.admin) {
throw new Error('Unauthorized');
}
if (updates.name !== undefined && !updates.name.trim()) {
throw new Error('Experiment name cannot be empty');
}
try {
const updateData: any = {};
if (updates.name !== undefined) {
updateData.name = updates.name.trim();
}
if (updates.description !== undefined) {
updateData.description = updates.description.trim() || null;
}
if (updates.annotationTool !== undefined) {
updateData.annotationTool = updates.annotationTool;
}
if (updates.published !== undefined) {
updateData.published = updates.published;
}
updateData.updatedAt = new Date();
const result = await db
.update(experiment)
.set(updateData)
.where(eq(experiment.id, id));
revalidatePath(`/admin/experiments/${id}`);
revalidatePath('/admin/experiments');
return result;
} catch (error) {
console.error('Error updating experiment:', error);
throw new Error('Failed to update experiment');
}
}
export async function deleteExperiment(id: number) {
const result = await requireAdmin();
if (!result.authenticated || !result.admin) {
throw new Error('Unauthorized');
}
try {
// Delete the experiment
await db.delete(experiment).where(eq(experiment.id, id));
revalidatePath('/admin/experiments');
return { success: true };
} catch (error) {
console.error('Error deleting experiment:', error);
throw new Error('Failed to delete experiment');
}
}
export async function getExperimentsByDataset(datasetId: number) {
const result = await requireAdmin();
if (!result.authenticated || !result.admin) {
throw new Error('Unauthorized');
}
try {
const experiments = await db
.select()
.from(experiment)
.where(eq(experiment.datasetId, datasetId));
return experiments;
} catch (error) {
console.error('Error fetching experiments:', error);
throw new Error('Failed to fetch experiments');
}
}
export async function getExperimentById(id: number) {
const session = await auth.api.getSession({ headers: await headers() });
if (!session) throw new Error('Nicht angemeldet');
try { const experimentData = await db
.select()
.from(experiment)
.where(
and(
eq(experiment.id, id),
eq(experiment.published, true)
))
.limit(1);
return experimentData[0] || null;
}
catch (error) {
console.error('Error fetching experiment:', error);
throw new Error('Failed to fetch experiment');
}
}