Skip to content

Commit

Permalink
feat: added in delete workspace functionality and tested it works [20…
Browse files Browse the repository at this point in the history
…25-01-19]
  • Loading branch information
CHRISCARLON committed Jan 19, 2025
1 parent c1517a7 commit 4442529
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use server";
import { getAuthToken } from "../lib/auth";
import { revalidatePath } from "next/cache";

export async function deleteWorkspace(workspaceId: string): Promise<void> {
const token = await getAuthToken();
if (!workspaceId) throw new Error("Workspace ID is required");

const response = await fetch(
`${process.env.GRIDWALK_API}/workspace/${workspaceId}`,
{
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
},
}
);

if (!response.ok) {
const errorText = await response.text();
if (response.status === 401) throw new Error("Authentication failed");
if (response.status === 403) throw new Error("Insufficient permissions");
if (response.status === 404) throw new Error("Workspace not found");
throw new Error(errorText || "Failed to delete workspace");
}

// Revalidate the projects list page to reflect the deletion
revalidatePath(`/workspaces`);
}
132 changes: 132 additions & 0 deletions gridwalk-ui/src/app/workspace/[workspaceId]/deleteWorkspaceModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"use client";
import React, { useState } from "react";

interface DeleteWorkspaceModalProps {
isOpen: boolean;
onClose: () => void;
workspaceName: string;
onConfirm: () => Promise<void>;
}

export const DeleteWorkspaceModal: React.FC<DeleteWorkspaceModalProps> = ({
isOpen,
onClose,
workspaceName,
onConfirm,
}) => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [confirmText, setConfirmText] = useState("");

const isDeleteEnabled = confirmText === "DELETE";

const handleDelete = async (e: React.FormEvent) => {
e.preventDefault();
if (!isDeleteEnabled) return;

setIsLoading(true);
setError(null);

try {
await onConfirm();
onClose();
setConfirmText("");
} catch (err) {
setError(err instanceof Error ? err.message : "An error occurred");
} finally {
setIsLoading(false);
}
};

if (!isOpen) return null;

return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-lg p-6 w-full max-w-md relative">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-bold text-gray-900">Delete Workspace</h2>
<button
onClick={onClose}
className="text-gray-500 hover:text-gray-700"
>
</button>
</div>

{error && (
<div className="bg-red-50 text-red-600 p-3 rounded-md mb-4">
{error}
</div>
)}

<div className="mb-6">
<p className="text-gray-700 mb-2">
Are you sure you want to delete{" "}
<span className="font-semibold">{workspaceName}</span>?
</p>
<p className="text-gray-600 text-sm mb-4">
This action cannot be undone. The workspace and all associated data
will be permanently removed.
</p>
<p className="text-gray-700 text-sm mb-2">
Type <span className="font-mono font-bold">DELETE</span> to confirm:
</p>
<input
type="text"
value={confirmText}
onChange={(e) => setConfirmText(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md text-gray-900 placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-red-500"
placeholder="Enter Text..."
disabled={isLoading}
/>
</div>

<form onSubmit={handleDelete}>
<div className="flex justify-end gap-2">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
disabled={isLoading}
>
Cancel
</button>
<button
type="submit"
disabled={isLoading || !isDeleteEnabled}
className="px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
>
{isLoading ? (
<>
<LoadingSpinner />
Deleting...
</>
) : (
"Delete Workspace"
)}
</button>
</div>
</form>
</div>
</div>
);
};

export const LoadingSpinner: React.FC = () => (
<svg className="animate-spin h-4 w-4" viewBox="0 0 24 24">
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
fill="none"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
);
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ import { CreateProjectModal, DeleteProjectModal } from "./projectModal";
import { HelpSupportModal } from "../supportModal";
import { AddWorkspaceMemberModal } from "./addMemberModal";
import { ViewWorkspaceMemberModal } from "./viewMembersModal";
import { DeleteWorkspaceModal } from "./deleteWorkspaceModal";
import { useWorkspaces } from "../workspaceContext";
import { createProject } from "./actions/projects/create";
import { deleteProject } from "./actions/projects/delete";
import { deleteWorkspace } from "./actions/workspace/delete_workspace";
import { addWorkspaceMember } from "./actions/workspace";
import { ViewWorkspaceConnectionsModal } from "./viewConnectionsModal";
import { useRouter } from "next/navigation";
Expand All @@ -43,6 +45,8 @@ export default function WorkspaceProjectsClient({
const { workspaces } = useWorkspaces();
const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false);
const [isConnectionDialogOpen, setIsConnectionDialogOpen] = useState(false);
const [isDeleteWorkspaceDialogOpen, setIsDeleteWorkspaceDialogOpen] =
useState(false);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isHelpSupportModalOpen, setIsHelpSupportModalOpen] = useState(false);
const [isMemberDialogOpen, setIsMemberDialogOpen] = useState(false);
Expand Down Expand Up @@ -90,6 +94,18 @@ export default function WorkspaceProjectsClient({
}
};

const handleDeleteWorkspace = async () => {
try {
await deleteWorkspace(workspaceId);
router.push("/workspace");
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(error.message || "Failed to delete workspace");
}
throw new Error("Failed to delete workspace");
}
};

const handleAddMember = async (email: string, role: "Admin" | "Read") => {
try {
await addWorkspaceMember({
Expand Down Expand Up @@ -173,7 +189,10 @@ export default function WorkspaceProjectsClient({
<Database size={16} />
View DB Connections
</button>
<button className="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors w-full text-left">
<button
onClick={() => setIsDeleteWorkspaceDialogOpen(true)}
className="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors w-full text-left"
>
<DeleteIcon size={16} />
Delete Workspace
</button>
Expand Down Expand Up @@ -244,6 +263,13 @@ export default function WorkspaceProjectsClient({
onSubmit={handleCreateProject}
/>

<DeleteWorkspaceModal
isOpen={isDeleteWorkspaceDialogOpen}
onClose={() => setIsDeleteWorkspaceDialogOpen(false)}
workspaceName={currentWorkspace?.name || ""}
onConfirm={handleDeleteWorkspace}
/>

<DeleteProjectModal
isOpen={isDeleteDialogOpen}
onClose={() => {
Expand Down

0 comments on commit 4442529

Please sign in to comment.