Skip to content

Commit

Permalink
feat: add support for pausing and resuming projects, refs #52
Browse files Browse the repository at this point in the history
feat: add DangerZone component to Overview page to handle project deletion and notice deletion

fix: change project prop to projectId in ConfirmationDialog component

fix: change handleDeleteProjectConfirm and handleDeleteProjectNoticesConfirm to not receive projectId as parameter

fix: add invalidateProjectsCache, invalidateProjectCache and invalidateAllProjectCache functions to invalidate cache when a project is deleted or paused

fix: toggleProjectPausedStatus function to toggle paused status of a project

fix: add paused field to Project model in Prisma schema

fix: add paused column to projects table in migration file
  • Loading branch information
masterkain committed May 29, 2023
1 parent 75403c2 commit 2680907
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 37 deletions.
13 changes: 6 additions & 7 deletions components/ConfirmationDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@

import { deleteProject, deleteProjectNotices } from '@/app/_actions';
import { Dialog, Transition } from '@headlessui/react';
import { Project } from '@prisma/client';
import { Fragment, useRef, useState, useTransition } from 'react';
import { SlDisc, SlFire } from 'react-icons/sl';
import { VscTrash } from 'react-icons/vsc';

export default function ConfirmationDialog({
project,
projectId,
title,
body,
btnId,
}: {
project: Project;
projectId: string;
title?: string;
body?: string;
btnId?: string;
Expand All @@ -22,14 +21,14 @@ export default function ConfirmationDialog({
const [isPending, startTransition] = useTransition();
const cancelButtonRef = useRef(null);

const handleDeleteProjectConfirm = async (projectId: string) => {
const handleDeleteProjectConfirm = async () => {
startTransition(async () => {
await deleteProject(projectId);
setOpen(false);
});
};

const handleDeleteProjectNoticesConfirm = async (projectId: string) => {
const handleDeleteProjectNoticesConfirm = async () => {
startTransition(async () => {
await deleteProjectNotices(projectId);
setOpen(false);
Expand All @@ -38,9 +37,9 @@ export default function ConfirmationDialog({

const handleConfirmAction = () => {
if (btnId === 'deleteProject') {
handleDeleteProjectConfirm(project.id);
handleDeleteProjectConfirm();
} else if (btnId === 'deleteAllErrors') {
handleDeleteProjectNoticesConfirm(project.id);
handleDeleteProjectNoticesConfirm();
}
};

Expand Down
23 changes: 3 additions & 20 deletions components/project/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import NoData from '@/components/NoData';
import { getNoticeIdsByProjectId } from '@/lib/queries/notices';
import { getHourlyOccurrenceRateForLast14Days, getOccurrenceIdsByNoticeIds } from '@/lib/queries/occurrences';
import { getProjectById } from '@/lib/queries/projects';
import ConfirmationDialog from '../ConfirmationDialog';
import OccurrencesChartWrapper from './OccurrencesChartWrapper';
import DangerZone from './cards/DangerZone';

type OverviewProps = {
projectId: string;
Expand Down Expand Up @@ -82,7 +82,7 @@ async function Overview({ projectId }: OverviewProps) {
))}
</div>
</div>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-1">
<div className="grid grid-cols-1 gap-6">
<div className="rounded-lg bg-gray-900 p-6">
<h3 className="text-base font-semibold leading-6 text-white">Test Zone</h3>
<p className="mt-1 text-sm leading-6 text-gray-400">Send test exceptions</p>
Expand All @@ -93,24 +93,7 @@ async function Overview({ projectId }: OverviewProps) {
</div>

<div className="rounded-lg bg-gray-900 p-6">
<h3 className="text-base font-semibold leading-6 text-rose-500">Danger Zone</h3>
<div className="mt-6 space-y-4">
<div className="grid grid-cols-1 justify-items-center gap-4">
<ConfirmationDialog
project={project}
title="Delete All Errors"
body={`Are you sure you want to delete all exceptions for the project "${project.name}"? This action cannot be undone.`}
btnId="deleteAllErrors"
/>

<ConfirmationDialog
project={project}
title="Delete Project"
body={`Are you sure you want to delete the project "${project.name}"? This action cannot be undone.`}
btnId="deleteProject"
/>
</div>
</div>
<DangerZone projectId={project.id} />
</div>
</div>
</div>
Expand Down
38 changes: 38 additions & 0 deletions components/project/cards/DangerZone.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import ConfirmationDialog from '@/components/ConfirmationDialog';
import { getProjectById } from '@/lib/queries/projects';

type DangerZoneProps = {
projectId: string;
};

async function DangerZone({ projectId }: DangerZoneProps) {
const project = await getProjectById(projectId);
if (!project) {
throw new Error('Project not found');
}

return (
<>
<h3 className="text-base font-semibold leading-6 text-rose-500">Danger Zone</h3>
<div className="mt-6 space-y-4">
<div className="grid grid-cols-1 justify-items-center gap-4">
<ConfirmationDialog
projectId={project.id}
title="Delete All Errors"
body={`Are you sure you want to delete all exceptions for the project "${project.name}"? This action cannot be undone.`}
btnId="deleteAllErrors"
/>

<ConfirmationDialog
projectId={project.id}
title="Delete Project"
body={`Are you sure you want to delete the project "${project.name}"? This action cannot be undone.`}
btnId="deleteProject"
/>
</div>
</div>
</>
);
}

export default DangerZone as unknown as (props: DangerZoneProps) => JSX.Element;
44 changes: 34 additions & 10 deletions lib/actions/projectActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,24 @@ function validateProjectName(name: string): boolean {
return nameRegex.test(name);
}

function invalidateProjectsCache(): void {
revalidatePath('/projects');
}

function invalidateProjectCache(projectId: string): void {
revalidatePath(`/projects/${projectId}`);
}

async function invalidateAllProjectCache(): Promise<void> {
const projects = await prisma.project.findMany({ select: { id: true } });
const projectIds = projects.map((project) => project.id);

await Promise.all([
...projectIds.map((id) => invalidateProjectCache(id)),
invalidateProjectsCache(),
]);
}

export async function createProject(data: FormData): Promise<CreateProjectResponse> {
const repository_url = data.get('repository_url') as string;

Expand Down Expand Up @@ -61,19 +79,25 @@ export async function createProject(data: FormData): Promise<CreateProjectRespon

export async function deleteProjectNotices(projectId: string): Promise<void> {
await prisma.notice.deleteMany({ where: { project_id: projectId } });
const projectIds = await prisma.project.findMany({
select: { id: true },
}).then((projects) => projects.map((project) => project.id));

// Run revalidatePath on each project ID in parallel
await Promise.all(
projectIds.map((id) => revalidatePath(`/projects/${id}`))
);
revalidatePath('/projects');
await invalidateAllProjectCache();
}

export async function deleteProject(projectId: string): Promise<void> {
await prisma.project.delete({ where: { id: projectId } });
revalidatePath('/projects');
invalidateProjectsCache();
redirect('/projects');
}

export async function toggleProjectPausedStatus(projectId: string): Promise<void> {
const project = await prisma.project.findUnique({ where: { id: projectId } });
if (!project) {
throw new Error('Project not found.');
}

await prisma.project.update({
where: { id: projectId },
data: { paused: !project.paused },
});

invalidateAllProjectCache();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "projects" ADD COLUMN "paused" BOOLEAN NOT NULL DEFAULT false;
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ model Project {
repo_issue_tracker String?
repo_url String?
notices_count BigInt @default(0)
paused Boolean @default(false)
created_at DateTime @default(now()) @db.Timestamp(6)
updated_at DateTime @updatedAt @db.Timestamp(6)
notices Notice[]
Expand Down

0 comments on commit 2680907

Please sign in to comment.