Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to delete traces #314

Merged
merged 3 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions frontend/app/api/projects/[projectId]/spans/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,35 @@ export async function GET(req: NextRequest, props: { params: Promise<{ projectId

return new Response(JSON.stringify(spanData), { status: 200 });
}


export async function DELETE(
req: NextRequest,
props: { params: Promise<{ projectId: string; spanId: string }> }
): Promise<Response> {
const params = await props.params;
const projectId = params.projectId;

const { searchParams } = new URL(req.url);
const spanId = searchParams.get('spanId')?.split(',');

if (!spanId) {
return new Response('At least one Span ID is required', { status: 400 });
}

try {
await db.delete(spans)
.where(
and(
inArray(spans.spanId, spanId),
eq(spans.projectId, projectId)
)
);

return new Response('Spans deleted successfully', { status: 200 });
} catch (error) {
return new Response('Error deleting spans', { status: 500 });
}
}


33 changes: 33 additions & 0 deletions frontend/app/api/projects/[projectId]/traces/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { and, eq, inArray} from 'drizzle-orm';
import { NextRequest } from 'next/server';
import { getServerSession } from 'next-auth';

import { authOptions } from '@/lib/auth';
import { db } from '@/lib/db/drizzle';
import { traces } from '@/lib/db/migrations/schema';
import { fetcher } from '@/lib/utils';

export async function GET(req: NextRequest, props: { params: Promise<{ projectId: string }> }): Promise<Response> {
Expand All @@ -21,3 +24,33 @@ export async function GET(req: NextRequest, props: { params: Promise<{ projectId
}
);
}


export async function DELETE(
req: NextRequest,
props: { params: Promise<{ projectId: string; traceId: string }> }
): Promise<Response> {
const params = await props.params;
const projectId = params.projectId;

const { searchParams } = new URL(req.url);
const traceId = searchParams.get('traceId')?.split(',');

if (!traceId) {
return new Response('At least one Trace ID is required', { status: 400 });
}

try {
await db.delete(traces)
.where(
and(
inArray(traces.id, traceId),
eq(traces.projectId, projectId)
)
);

return new Response('Traces deleted successfully', { status: 200 });
} catch (error) {
return new Response('Error deleting traces', { status: 500 });
}
}
48 changes: 6 additions & 42 deletions frontend/components/dataset/dataset.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
'use client';

import { ColumnDef } from '@tanstack/react-table';
import { Loader2, Trash2 } from 'lucide-react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { Resizable } from 're-resizable';
import { useEffect, useState } from 'react';
import useSWR from 'swr';

import { Button } from '@/components/ui/button';
import { DataTable } from '@/components/ui/datatable';
import DeleteSelectedRows from '@/components/ui/DeleteSelectedRows';
import { useProjectContext } from '@/contexts/project-context';
import { Datapoint, Dataset as DatasetType } from '@/lib/dataset/types';
import { useToast } from '@/lib/hooks/use-toast';
import { PaginatedResponse } from '@/lib/types';
import { swrFetcher } from '@/lib/utils';

import ClientTimestampFormatter from '../client-timestamp-formatter';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '../ui/dialog';
import DownloadButton from '../ui/download-button';
import Header from '../ui/header';
import MonoWithCopy from '../ui/mono-with-copy';
Expand All @@ -44,8 +34,6 @@ export default function Dataset({ dataset }: DatasetProps) {
const { projectId } = useProjectContext();
const { toast } = useToast();
const [datapoints, setDatapoints] = useState<Datapoint[] | undefined>(undefined);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);

// Get datapointId from URL params
const datapointId = searchParams.get('datapointId');
Expand Down Expand Up @@ -115,7 +103,6 @@ export default function Dataset({ dataset }: DatasetProps) {
];

const handleDeleteDatapoints = async (datapointIds: string[]) => {
setIsDeleting(true);
const response = await fetch(
`/api/projects/${projectId}/datasets/${dataset.id}/datapoints` +
`?datapointIds=${datapointIds.join(',')}` +
Expand Down Expand Up @@ -143,9 +130,6 @@ export default function Dataset({ dataset }: DatasetProps) {
if (selectedDatapoint && datapointIds.includes(selectedDatapoint.id)) {
handleDatapointSelect(null);
}

setIsDeleting(false);
setIsDeleteDialogOpen(false);
};

// Update URL when datapoint is selected
Expand Down Expand Up @@ -218,31 +202,11 @@ export default function Dataset({ dataset }: DatasetProps) {
enableRowSelection
selectionPanel={(selectedRowIds) => (
<div className="flex flex-col space-y-2">
<Dialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
<DialogTrigger asChild>
<Button variant="ghost">
<Trash2 size={12} />
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete Datapoints</DialogTitle>
<DialogDescription>
Are you sure you want to delete
{selectedRowIds.length} datapoint(s)? This action cannot be undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" onClick={() => setIsDeleteDialogOpen(false)} disabled={isDeleting}>
Cancel
</Button>
<Button onClick={() => handleDeleteDatapoints(selectedRowIds)} disabled={isDeleting}>
{isDeleting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Delete
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<DeleteSelectedRows
selectedRowIds={selectedRowIds}
onDelete={handleDeleteDatapoints}
entityName="datapoints"
/>
</div>
)}
/>
Expand Down
46 changes: 6 additions & 40 deletions frontend/components/datasets/datasets.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
'use client';

import { ColumnDef } from '@tanstack/react-table';
import { Loader2, Trash2 } from 'lucide-react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';

import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@/components/ui/dialog';
import DeleteSelectedRows from '@/components/ui/DeleteSelectedRows';
import { useProjectContext } from '@/contexts/project-context';
import { DatasetInfo } from '@/lib/dataset/types';
import { useToast } from '@/lib/hooks/use-toast';
Expand Down Expand Up @@ -63,12 +53,9 @@ export default function Datasets() {

const pageCount = Math.ceil(totalCount / pageSize);

const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const { toast } = useToast();

const handleDeleteDatasets = async (datasetIds: string[]) => {
setIsDeleting(true);
try {
const res = await fetch(
`/api/projects/${projectId}/datasets?datasetIds=${datasetIds.join(',')}`,
Expand All @@ -93,8 +80,6 @@ export default function Datasets() {
variant: 'destructive',
});
}
setIsDeleting(false);
setIsDeleteDialogOpen(false);
};

const columns: ColumnDef<DatasetInfo>[] = [
Expand Down Expand Up @@ -153,30 +138,11 @@ export default function Datasets() {
totalItemsCount={totalCount}
selectionPanel={(selectedRowIds) => (
<div className="flex flex-col space-y-2">
<Dialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
<DialogTrigger asChild>
<Button variant="ghost">
<Trash2 size={12} />
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete Datasets</DialogTitle>
<DialogDescription>
Are you sure you want to delete {selectedRowIds.length} dataset(s)? This action cannot be undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" onClick={() => setIsDeleteDialogOpen(false)} disabled={isDeleting}>
Cancel
</Button>
<Button onClick={() => handleDeleteDatasets(selectedRowIds)} disabled={isDeleting}>
{isDeleting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Delete
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<DeleteSelectedRows
selectedRowIds={selectedRowIds}
onDelete={handleDeleteDatasets}
entityName="datasets"
/>
</div>
)}
emptyRow={
Expand Down
47 changes: 6 additions & 41 deletions frontend/components/evaluations/evaluations.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use client';

import { ColumnDef } from '@tanstack/react-table';
import { Loader2, Trash2 } from 'lucide-react';
import { useRouter, useSearchParams } from 'next/navigation';
import { usePostHog } from 'posthog-js/react';
import { useState } from 'react';
import useSWR from 'swr';

import DeleteSelectedRows from '@/components/ui/DeleteSelectedRows';
import { useProjectContext } from '@/contexts/project-context';
import { useUserContext } from '@/contexts/user-context';
import { AggregationFunction } from '@/lib/clickhouse/utils';
Expand All @@ -17,17 +17,7 @@ import { PaginatedResponse } from '@/lib/types';
import { swrFetcher } from '@/lib/utils';

import ClientTimestampFormatter from '../client-timestamp-formatter';
import { Button } from '../ui/button';
import { DataTable } from '../ui/datatable';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '../ui/dialog';
import Header from '../ui/header';
import Mono from '../ui/mono';
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '../ui/resizable';
Expand Down Expand Up @@ -82,12 +72,9 @@ export default function Evaluations() {
}
];

const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const { toast } = useToast();

const handleDeleteEvaluations = async (selectedRowIds: string[]) => {
setIsDeleting(true);
try {
const response = await fetch(
`/api/projects/${projectId}/evaluations?evaluationIds=${selectedRowIds.join(',')}`,
Expand Down Expand Up @@ -116,8 +103,6 @@ export default function Evaluations() {
variant: 'destructive',
});
}
setIsDeleting(false);
setIsDeleteDialogOpen(false);
};

return (
Expand Down Expand Up @@ -166,31 +151,11 @@ export default function Evaluations() {
manualPagination
selectionPanel={(selectedRowIds) => (
<div className="flex flex-col space-y-2">
<Dialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
<DialogTrigger asChild>
<Button variant="ghost">
<Trash2 size={12} />
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete Evaluations</DialogTitle>
<DialogDescription>
Are you sure you want to delete {selectedRowIds.length} evaluation(s)?
This action cannot be undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" onClick={() => setIsDeleteDialogOpen(false)} disabled={isDeleting}>
Cancel
</Button>
<Button onClick={() => handleDeleteEvaluations(selectedRowIds)} disabled={isDeleting}>
{isDeleting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Delete
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<DeleteSelectedRows
selectedRowIds={selectedRowIds}
onDelete={handleDeleteEvaluations}
entityName="evaluations"
/>
</div>
)}
/>
Expand Down
Loading
Loading