Skip to content

Commit

Permalink
Add ability to rename projects (#306)
Browse files Browse the repository at this point in the history
* Add ability to rename projects

* Added user feedback notifiaction and convert PUT to POST

* change to router.refresh

* add close dialog after rename
  • Loading branch information
nidhi-wa authored Jan 9, 2025
1 parent 8114f11 commit 29f2ba6
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 0 deletions.
36 changes: 36 additions & 0 deletions frontend/app/api/projects/[projectId]/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
import { eq } from 'drizzle-orm';
import { getServerSession } from 'next-auth';

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

export async function POST(
req: Request,
{ params }: { params: { projectId: string } }
): Promise<Response> {
const { projectId } = params;
const { name } = await req.json();

if (!name) {
return new Response(JSON.stringify({ error: 'Project name is required.' }), {
status: 400,
});
}

try {
const result = await db.update(projects).set({ name }).where(eq(projects.id, projectId));

if (result.count === 0) {
return new Response(JSON.stringify({ error: 'Project not found.' }), {
status: 404,
});
}

return new Response(JSON.stringify({ message: 'Project renamed successfully.' }), {
status: 200,
});
} catch (error) {
return new Response(JSON.stringify({ error: 'Internal server error.' }), {
status: 500,
});
}
}

export async function DELETE(
req: Request,
{ params }: { params: { projectId: string } }
Expand All @@ -19,3 +54,4 @@ export async function DELETE(
}
});
}

124 changes: 124 additions & 0 deletions frontend/components/settings/rename-project.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
'use client';

import { Edit,Loader2 } from 'lucide-react';
import { useRouter } from "next/navigation";
import { useState } from 'react';

import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@/components/ui/dialog';
import { useProjectContext } from '@/contexts/project-context';
import { useToast } from '@/lib/hooks/use-toast';
import { cn } from '@/lib/utils';

import { Button } from '../ui/button';
import { Input } from '../ui/input';
import { Label } from '../ui/label';

interface RenameProjectProps {}

export default function RenameProject({}: RenameProjectProps) {
const { projectId, projectName } = useProjectContext();
const router = useRouter();

const [newProjectName, setNewProjectName] = useState<string>('');
const [isRenameDialogOpen, setIsRenameDialogOpen] = useState(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const { toast } = useToast();

const renameProject = async () => {
setIsLoading(true);

const res = await fetch(`/api/projects/${projectId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: newProjectName,
}),
});

if (res.ok) {
toast({
title: 'Project Renamed',
description: `Project renamed successfully!.`,
});
router.refresh();
setIsRenameDialogOpen(false);
} else {
toast({
title: 'Error',
description: 'Something went wrong. Please try again later.',
});
}

setIsLoading(false);
};

return (
<div>
<div className="flex flex-col items-start space-y-4">
<h1 className="text-lg">Rename project</h1>
<Label className="text-sm text-secondary-foreground">
Update the name of your project. Changes will take effect immediately.
</Label>
<Dialog
open={isRenameDialogOpen}
onOpenChange={() => {
setIsRenameDialogOpen(!isRenameDialogOpen);
setNewProjectName('');
}}
>
<DialogTrigger asChild>
<Button
onClick={() => {
setIsRenameDialogOpen(true);
}}
variant="outline"
className="h-8 max-w-80"
>
<Edit className="w-4 mr-1" />
Rename project
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Rename project</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<Label>Enter new project name</Label>
<Input
autoFocus
placeholder={projectName}
value={newProjectName}
onChange={(e) => setNewProjectName(e.target.value)}
/>
</div>
<DialogFooter>
<Button
disabled={!newProjectName.trim() || isLoading}
onClick={renameProject}
handleEnter={true}
>
<Loader2
className={cn(
'mr-2 hidden',
isLoading ? 'animate-spin block' : ''
)}
size={16}
/>
Rename
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
);
}
2 changes: 2 additions & 0 deletions frontend/components/settings/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Header from "../ui/header";
import DeleteProject from "./delete-project";
import ProjectApiKeys from "./project-api-keys";
import ProviderApiKeys from "./provider-api-keys";
import RenameProject from "./rename-project";

interface SettingsProps {
apiKeys: ProjectApiKey[];
Expand All @@ -18,6 +19,7 @@ export default function Settings({ apiKeys }: SettingsProps) {
<div className="flex flex-col space-y-8 p-4">
<ProjectApiKeys apiKeys={apiKeys} />
<ProviderApiKeys />
<RenameProject />
<DeleteProject />
</div>
</div>
Expand Down

0 comments on commit 29f2ba6

Please sign in to comment.