diff --git a/app/occurrences/[occurrence_id]/page.tsx b/app/occurrences/[occurrence_id]/page.tsx
index 6447a2ed..c9733a5b 100644
--- a/app/occurrences/[occurrence_id]/page.tsx
+++ b/app/occurrences/[occurrence_id]/page.tsx
@@ -9,6 +9,7 @@ import BookmarkButton from '@/components/occurrence/BookmarkButton';
import Context from '@/components/occurrence/Context';
import Environment from '@/components/occurrence/Environment';
import Params from '@/components/occurrence/Params';
+import ResolveButton from '@/components/occurrence/ResolveButton';
import Session from '@/components/occurrence/Session';
import Toolbox from '@/components/occurrence/Toolbox';
import ProjectActionsMenu from '@/components/project/ActionsMenu';
@@ -164,6 +165,7 @@ export default async function Occurrence({ params, searchParams }: ComponentProp
Last seen: {occurrence.updated_at.toUTCString()}
+
diff --git a/components/OccurrencesTable.tsx b/components/OccurrencesTable.tsx
index a17dd224..87b601a5 100644
--- a/components/OccurrencesTable.tsx
+++ b/components/OccurrencesTable.tsx
@@ -35,6 +35,12 @@ export default async function OccurrencesTable({ noticeId, searchParams }: Occur
+ {occurrence.resolved_at && (
+
+ resolved
+
+ )}
+
First seen:
diff --git a/components/occurrence/ResolveButton.tsx b/components/occurrence/ResolveButton.tsx
new file mode 100644
index 00000000..e4bfb51d
--- /dev/null
+++ b/components/occurrence/ResolveButton.tsx
@@ -0,0 +1,26 @@
+'use client';
+
+import { reinstateOccurrence, resolveOccurrence } from '@/app/_actions';
+import { useTransition } from 'react';
+
+export default function ResolveButton({ occurrenceId, resolvedAt }: { occurrenceId: string; resolvedAt: Date | null }) {
+ let [isPending, startTransition] = useTransition();
+
+ return resolvedAt ? (
+
+ ) : (
+
+ );
+}
diff --git a/lib/actions/occurrenceActions.ts b/lib/actions/occurrenceActions.ts
index 7586f39b..73610555 100644
--- a/lib/actions/occurrenceActions.ts
+++ b/lib/actions/occurrenceActions.ts
@@ -86,3 +86,21 @@ export async function removeOccurrenceBookmark(occurrenceId: string) {
revalidatePath(`/occurrences/${occurrenceId}`)
revalidatePath('/bookmarks')
}
+
+export async function resolveOccurrence(occurrenceId: string) {
+ await prisma.occurrence.update({
+ where: { id: occurrenceId },
+ data: { resolved_at: new Date() },
+ });
+
+ revalidatePath(`/occurrences/${occurrenceId}`);
+}
+
+export async function reinstateOccurrence(occurrenceId: string) {
+ await prisma.occurrence.update({
+ where: { id: occurrenceId },
+ data: { resolved_at: null },
+ });
+
+ revalidatePath(`/occurrences/${occurrenceId}`);
+}
diff --git a/lib/processError.ts b/lib/processError.ts
index fabf9df3..ba16d619 100644
--- a/lib/processError.ts
+++ b/lib/processError.ts
@@ -86,6 +86,7 @@ export async function processError(
},
},
update: {
+ resolved_at: null, // auto reinstate
// we've seen updated_at work here
seen_count: {
increment: 1,
diff --git a/prisma/migrations/20230608180606_add_resolved_at/migration.sql b/prisma/migrations/20230608180606_add_resolved_at/migration.sql
new file mode 100644
index 00000000..491d76d0
--- /dev/null
+++ b/prisma/migrations/20230608180606_add_resolved_at/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "occurrences" ADD COLUMN "resolved_at" TIMESTAMP(6);
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 016fc1d1..5f6cb0db 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -59,6 +59,7 @@ model Occurrence {
params Json @default("{}")
created_at DateTime @default(now()) @db.Timestamp(6)
updated_at DateTime @updatedAt @db.Timestamp(6)
+ resolved_at DateTime? @db.Timestamp(6)
notice Notice @relation(fields: [notice_id], references: [id], onDelete: Cascade)
hourly_occurrences HourlyOccurrence[]
bookmarks OccurrenceBookmark[]