Skip to content

Commit

Permalink
Merge pull request #58 from diggerhq/feat/delete-project
Browse files Browse the repository at this point in the history
Delete projects
  • Loading branch information
ZIJ authored Sep 20, 2024
2 parents 9efa9b2 + 86b072d commit c792a06
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
'use client';

import { Button } from '@/components/ui/button';
import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { deleteProject } from '@/data/user/projects';
import { useSAToastMutation } from '@/hooks/useSAToastMutation';
import { zodResolver } from '@hookform/resolvers/zod';
import { motion } from 'framer-motion';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'sonner';
import { z } from 'zod';

type DeleteProjectProps = {
projectName: string;
projectId: string;
};

export const DeleteProject = ({
projectName,
projectId,
}: DeleteProjectProps) => {
const [open, setOpen] = useState(false);
const router = useRouter();

const formSchema = z.object({
projectName: z
.string()
.refine(
(v) => v === `delete ${projectName}`,
`Must match "delete ${projectName}"`,
),
});

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
projectName: '',
},
});

const { mutate, isLoading } = useSAToastMutation(
async () => deleteProject(projectId),
{
onSuccess: () => {
toast.success('Project deleted');
setOpen(false);
router.push('/dashboard');
},
loadingMessage: 'Deleting project...',
errorMessage(error) {
try {
if (error instanceof Error) {
return String(error.message);
}
return `Failed to delete project ${String(error)}`;
} catch (_err) {
console.warn(_err);
return 'Failed to delete project';
}
},
},
);

const onSubmit = (values: z.infer<typeof formSchema>) => {
mutate();
};

return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className="mt-4"
>
<Card className="w-full max-w-5xl border-destructive/50 bg-destructive/5">
<CardHeader>
<CardTitle>
Danger Zone
</CardTitle>
<CardDescription>
Deleting a project does not destroy associated resources! Make sure to destroy them.
</CardDescription>
</CardHeader>
<CardFooter className="flex justify-start">
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="destructive">Delete Project</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete Project</DialogTitle>
<DialogDescription>
Type <strong>"delete {projectName}"</strong> to confirm.
</DialogDescription>
</DialogHeader>
<FormProvider {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="projectName"
render={({ field }) => (
<FormItem>
<FormLabel>Confirmation</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter>
<Button type="button" variant="outline" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button
type="submit"
variant="destructive"
disabled={isLoading || !form.formState.isValid}
>
{isLoading ? 'Deleting...' : 'Delete'} Project
</Button>
</DialogFooter>
</form>
</FormProvider>
</DialogContent>
</Dialog>
</CardFooter>
</Card>
</motion.div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Tables } from "@/lib/database.types";
import { motion } from "framer-motion";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { DeleteProject } from "./DeleteProject";

type ProjectSettingsProps = {
project: Tables<'projects'>;
Expand Down Expand Up @@ -291,6 +292,7 @@ export default function ProjectSettings({ project, repositoryName }: ProjectSett
</form>
</CardContent>
</Card>
<DeleteProject projectName={project.name} projectId={project.id} />
</motion.div>
);
}
23 changes: 18 additions & 5 deletions src/data/user/projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ export async function getProjectsForUser({
.from('projects')
.select('*, teams(name)')
.eq('organization_id', organizationId)
.ilike('name', `%${query}%`);
.ilike('name', `%${query}%`)
.is('deleted_at', null);

if (userRole !== 'admin' || userId !== 'owner') {
// For non-admin users, get their team memberships
Expand Down Expand Up @@ -333,7 +334,8 @@ export async function getProjectsListForUser({
.from('projects')
.select('id, name, slug, latest_action_on, created_at, repo_id, latest_drift_output')
.eq('organization_id', organizationId)
.ilike('name', `%${query}%`);
.ilike('name', `%${query}%`)
.is('deleted_at', null);

if (userRole !== 'admin' || userId !== 'owner') {
// For non-admin users, get their team memberships
Expand Down Expand Up @@ -399,7 +401,8 @@ export async function getSlimProjectsForUser({
let supabaseQuery = supabase
.from('projects')
.select('id,name, slug, latest_action_on, created_at, repo_id')
.in('id', projectIds);
.in('id', projectIds)
.is('deleted_at', null);

if (userRole !== 'admin' || userId !== 'owner') {
// For non-admin users, get their team memberships
Expand Down Expand Up @@ -463,7 +466,8 @@ export async function getProjectsIdsListForUser({
.from('projects')
.select('id,name, slug, latest_action_on, created_at, repo_id')
.eq('organization_id', organizationId)
.ilike('name', `%${query}%`);
.ilike('name', `%${query}%`)
.is('deleted_at', null);

if (userRole !== 'admin' || userId !== 'owner') {
// For non-admin users, get their team memberships
Expand Down Expand Up @@ -538,7 +542,8 @@ export async function getProjectsCountForUser({
.from('projects')
.select('*', { count: 'exact', head: true })
.eq('organization_id', organizationId)
.ilike('name', `%${query}%`);
.ilike('name', `%${query}%`)
.is('deleted_at', null);

if (userRole.member_role !== 'admin') {
// For non-admin users, get their team memberships
Expand Down Expand Up @@ -585,6 +590,7 @@ export const getAllProjectsInOrganization = async ({
.from("projects")
.select("*")
.eq("organization_id", organizationId)
.is('deleted_at', null)
.range(zeroIndexedPage * limit, (zeroIndexedPage + 1) * limit - 1);

if (query) {
Expand All @@ -609,6 +615,7 @@ export const getAllProjectIdsInOrganization = async (organizationId: string) =>
.from("projects")
.select("id")
.eq("organization_id", organizationId)
.is('deleted_at', null)
.order("created_at", { ascending: false });

const { data, error } = await supabaseQuery;
Expand All @@ -626,6 +633,7 @@ export const getProjectIdsInOrganization = async (organizationId: string, count:
const supabaseQuery = supabase
.from("projects")
.select("id")
.is('deleted_at', null)
.eq("organization_id", organizationId);

const { data, error } = await supabaseQuery;
Expand Down Expand Up @@ -660,6 +668,7 @@ export const getOrganizationLevelProjects = async ({
.select("*")
.eq("organization_id", organizationId)
.is('team_id', null)
.is('deleted_at', null)
.range(zeroIndexedPage * limit, (zeroIndexedPage + 1) * limit - 1);

if (query) {
Expand Down Expand Up @@ -698,6 +707,7 @@ export const getProjects = async ({
.from("projects")
.select("*")
.eq("organization_id", organizationId)
.is('deleted_at', null)
.range(zeroIndexedPage * limit, (zeroIndexedPage + 1) * limit - 1);

// Add team filter
Expand Down Expand Up @@ -742,6 +752,7 @@ export const getAllProjectsListInOrganization = async ({
.from("projects")
.select("id,name, slug, latest_action_on, created_at, repo_id")
.eq("organization_id", organizationId)
.is('deleted_at', null)
.range(zeroIndexedPage * limit, (zeroIndexedPage + 1) * limit - 1);

if (query) {
Expand Down Expand Up @@ -793,6 +804,7 @@ export const getProjectsList = async ({
.from("projects")
.select("id,name, slug, latest_action_on, created_at, repo_id")
.eq("organization_id", organizationId)
.is('deleted_at', null)
.range(zeroIndexedPage * limit, (zeroIndexedPage + 1) * limit - 1);

// Add team filter
Expand Down Expand Up @@ -891,6 +903,7 @@ export const getProjectsForUserTotalCount = async ({
head: true,
})
.eq("organization_id", organizationId)
.is('deleted_at', null)
.range(zeroIndexedPage * limit, (zeroIndexedPage + 1) * limit - 1);

if (query) {
Expand Down

0 comments on commit c792a06

Please sign in to comment.