Skip to content

Commit

Permalink
Delete custom project implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
atrincas committed Dec 23, 2024
1 parent 3117700 commit 2fefa68
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 7 deletions.
96 changes: 89 additions & 7 deletions client/src/containers/my-projects/columns.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useCallback } from "react";

import Link from "next/link";

import {
Expand All @@ -8,9 +10,11 @@ import {
import { ACTIVITY } from "@shared/entities/activity.enum";
import { CustomProject as CustomProjectEntity } from "@shared/entities/custom-project.entity";
import { Table as TableInstance, Row, ColumnDef } from "@tanstack/react-table";
import { useSession } from "next-auth/react";

import { formatCurrency } from "@/lib/format";
import { cn } from "@/lib/utils";
import { client } from "@/lib/query-client";
import { cn, getAuthHeader } from "@/lib/utils";

import { useFeatureFlags } from "@/hooks/use-feature-flags";

Expand All @@ -23,15 +27,85 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useToast } from "@/components/ui/toast/use-toast";

type CustomProject = Partial<CustomProjectEntity>;

type CustomColumn = ColumnDef<CustomProject, keyof CustomProject> & {
className?: string;
};

const ActionsDropdown = () => {
const ActionsDropdown = ({
instance,
}: {
instance: TableInstance<CustomProject> | Row<CustomProject>;
}) => {
const { "update-selection": updateSelection } = useFeatureFlags();
const isHeader = "getSelectedRowModel" in instance;
const deleteLabel = isHeader ? "Delete selection" : "Delete project";
const { data: session } = useSession();
const { toast } = useToast();
const deleteCustomProject = useCallback(
async (id: string): Promise<boolean> => {
try {
const { status } =
await client.customProjects.deleteCustomProject.mutation({
params: {
id,
},
extraHeaders: {
...getAuthHeader(session?.accessToken as string),
},
});

return status === 200;
} catch (e) {
return false;
}
},
[session?.accessToken],
);

const handleDelete = async () => {
const results: boolean[] = [];

if (isHeader) {
const selectedRows = (
instance as TableInstance<CustomProject>
).getSelectedRowModel().rows;

for (const row of selectedRows) {
const result = await deleteCustomProject(row.original.id as string);
results.push(result);
}
} else if (instance.original.id) {
const result = await deleteCustomProject(instance.original.id);
results.push(result);
}

const successCount = results.filter(Boolean).length;
const failureCount = results.length - successCount;

if (successCount > 0) {
toast({
description:
successCount === 1
? "Project deleted successfully"
: `${successCount} projects deleted successfully`,
});
}

if (failureCount > 0) {
toast({
variant: "destructive",
description:
failureCount === 1
? "Failed to delete project"
: `Failed to delete ${failureCount} projects`,
});
}
};

return (
<div className="flex w-full justify-end">
<DropdownMenu>
Expand All @@ -47,9 +121,17 @@ const ActionsDropdown = () => {
Update selection
</DropdownMenuItem>
)}
<DropdownMenuItem>
<TrashIcon className="mr-1 h-6 w-6" />
Delete selection
<DropdownMenuItem
className="cursor-pointer space-x-2 text-sm font-normal"
disabled={
isHeader &&
(instance as TableInstance<CustomProject>).getSelectedRowModel()
.rows.length === 0
}
onClick={handleDelete}
>
<TrashIcon className="h-4 w-4" />
{deleteLabel}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
Expand Down Expand Up @@ -125,8 +207,8 @@ export const columns: CustomColumn[] = [
},
{
accessorKey: "actions",
header: ActionsDropdown,
cell: ActionsDropdown,
header: ({ table }) => <ActionsDropdown instance={table} />,
cell: ({ row }) => <ActionsDropdown instance={row} />,
className: "!border-l-0",
enableSorting: false,
},
Expand Down
8 changes: 8 additions & 0 deletions shared/contracts/custom-projects.contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ export const customProjectContract = contract.router({
},
body: contract.type<CustomProject>(),
},
deleteCustomProject: {
method: "DELETE",
path: "/custom-projects/:id",
responses: {
200: contract.type<null>(),
},
body: null,
},
});

// TODO: Due to dificulties crafting a deeply nested conditional schema, I will go forward with nestjs custom validation pipe for now

0 comments on commit 2fefa68

Please sign in to comment.