chore: update frontend modules

This commit is contained in:
Steven
2023-06-22 18:07:28 +08:00
parent bd627fb250
commit 98fb1264c3
27 changed files with 22 additions and 3884 deletions

View File

@@ -6,7 +6,6 @@ import useLoading from "../hooks/useLoading";
import Icon from "./Icon";
interface Props {
workspaceId: WorkspaceId;
shortcutId?: ShortcutId;
onClose: () => void;
onConfirm?: () => void;
@@ -17,10 +16,9 @@ interface State {
}
const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
const { onClose, onConfirm, workspaceId, shortcutId } = props;
const { onClose, onConfirm, shortcutId } = props;
const [state, setState] = useState<State>({
shortcutCreate: {
workspaceId: workspaceId,
name: "",
link: "",
description: "",
@@ -36,7 +34,6 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
setState({
...state,
shortcutCreate: Object.assign(state.shortcutCreate, {
workspaceId: shortcutTemp.workspaceId,
name: shortcutTemp.name,
link: shortcutTemp.link,
description: shortcutTemp.description,

View File

@@ -1,143 +0,0 @@
import { Button, Input, Modal, ModalDialog } from "@mui/joy";
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { workspaceService } from "../services";
import useLoading from "../hooks/useLoading";
import Icon from "./Icon";
interface Props {
workspaceId?: WorkspaceId;
onClose: () => void;
onConfirm?: (workspace: Workspace) => void;
}
interface State {
workspaceCreate: WorkspaceCreate;
}
const CreateWorkspaceDialog: React.FC<Props> = (props: Props) => {
const { onClose, onConfirm, workspaceId } = props;
const [state, setState] = useState<State>({
workspaceCreate: {
name: "",
title: "",
description: "",
},
});
const requestState = useLoading(false);
useEffect(() => {
if (workspaceId) {
const workspaceTemp = workspaceService.getWorkspaceById(workspaceId);
if (workspaceTemp) {
setState({
...state,
workspaceCreate: Object.assign(state.workspaceCreate, {
name: workspaceTemp.name,
title: workspaceTemp.title,
description: workspaceTemp.description,
}),
});
}
}
}, [workspaceId]);
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>, key: string) => {
const text = e.target.value as string;
const tempObject = {} as any;
tempObject[key] = text;
setState({
...state,
workspaceCreate: Object.assign(state.workspaceCreate, tempObject),
});
};
const handleSaveBtnClick = async () => {
if (!state.workspaceCreate.name) {
toast.error("ID is required");
return;
}
if (!state.workspaceCreate.title) {
toast.error("Title is required");
return;
}
requestState.setLoading();
try {
let workspace;
if (workspaceId) {
workspace = await workspaceService.patchWorkspace({
id: workspaceId,
...state.workspaceCreate,
});
} else {
workspace = await workspaceService.createWorkspace({
...state.workspaceCreate,
});
}
if (onConfirm) {
onConfirm(workspace);
} else {
onClose();
}
} catch (error: any) {
console.error(error);
toast.error(JSON.stringify(error.response.data));
}
requestState.setFinish();
};
return (
<Modal open={true}>
<ModalDialog>
<div className="flex flex-row justify-between items-center w-80">
<span className="text-lg font-medium">{workspaceId ? "Edit Workspace" : "Create Workspace"}</span>
<Button variant="plain" onClick={onClose}>
<Icon.X className="w-5 h-auto text-gray-600" />
</Button>
</div>
<div>
<div className="w-full flex flex-col justify-start items-start mb-3">
<span className="mb-2">
ID <span className="text-red-600">*</span>
</span>
<Input
className="w-full"
type="text"
placeholder="Workspace ID is an unique identifier for your workspace."
value={state.workspaceCreate.name}
onChange={(e) => handleInputChange(e, "name")}
/>
</div>
<div className="w-full flex flex-col justify-start items-start mb-3">
<span className="mb-2">
Name <span className="text-red-600">*</span>
</span>
<Input className="w-full" type="text" value={state.workspaceCreate.title} onChange={(e) => handleInputChange(e, "title")} />
</div>
<div className="w-full flex flex-col justify-start items-start mb-3">
<span className="mb-2">Description</span>
<Input
className="w-full"
type="text"
value={state.workspaceCreate.description}
onChange={(e) => handleInputChange(e, "description")}
/>
</div>
<div className="w-full flex flex-row justify-end items-center mt-4 space-x-2">
<Button color="neutral" variant="plain" disabled={requestState.isLoading} loading={requestState.isLoading} onClick={onClose}>
Cancel
</Button>
<Button color="primary" disabled={requestState.isLoading} loading={requestState.isLoading} onClick={handleSaveBtnClick}>
Save
</Button>
</div>
</div>
</ModalDialog>
</Modal>
);
};
export default CreateWorkspaceDialog;

View File

@@ -1,31 +1,12 @@
import { useState } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom";
import { useAppSelector } from "../store";
import { userService } from "../services";
import Icon from "./Icon";
import Dropdown from "./common/Dropdown";
import CreateWorkspaceDialog from "./CreateWorkspaceDialog";
interface State {
showCreateWorkspaceDialog: boolean;
}
const Header: React.FC = () => {
const params = useParams();
const navigate = useNavigate();
const { user } = useAppSelector((state) => state.user);
const { workspaceList } = useAppSelector((state) => state.workspace);
const [state, setState] = useState<State>({
showCreateWorkspaceDialog: false,
});
const activedWorkspace = workspaceList.find((workspace) => workspace.name === params.workspaceName ?? "");
const handleCreateWorkspaceButtonClick = () => {
setState({
...state,
showCreateWorkspaceDialog: true,
});
};
const handleSignOutButtonClick = async () => {
await userService.doSignOut();
@@ -41,43 +22,6 @@ const Header: React.FC = () => {
<img src="/logo.png" className="w-8 h-auto mr-2" alt="" />
Shortify
</Link>
{workspaceList.length > 0 && activedWorkspace !== undefined && (
<>
<span className="font-mono mx-1 text-gray-200">/</span>
<Dropdown
trigger={
<button className="flex flex-row justify-end items-center cursor-pointer">
<span className="font-mono">{activedWorkspace?.title}</span>
<Icon.ChevronDown className="ml-1 w-5 h-auto text-gray-600" />
</button>
}
actions={
<>
{workspaceList.map((workspace) => {
return (
<Link
key={workspace.id}
to={`/${workspace.name}`}
className="w-full px-3 leading-10 flex flex-row justify-between items-center text-left cursor-pointer rounded whitespace-nowrap hover:bg-gray-100"
>
<span className="truncate">{workspace.title}</span>
{workspace.name === activedWorkspace?.name && <Icon.Check className="w-4 h-auto ml-1 shrink-0" />}
</Link>
);
})}
<hr className="w-full border-t my-1 border-t-gray-100" />
<button
className="w-full flex flex-row justify-start items-center px-3 leading-10 rounded cursor-pointer hover:bg-gray-100"
onClick={handleCreateWorkspaceButtonClick}
>
<Icon.Plus className="w-4 h-auto mr-2" /> Create Workspace
</button>
</>
}
actionsClassName="!w-48 !-left-4"
></Dropdown>
</>
)}
</div>
<div className="relative flex-shrink-0">
{user ? (
@@ -114,24 +58,6 @@ const Header: React.FC = () => {
</div>
</div>
</div>
{state.showCreateWorkspaceDialog && (
<CreateWorkspaceDialog
onClose={() => {
setState({
...state,
showCreateWorkspaceDialog: false,
});
}}
onConfirm={(workspace: Workspace) => {
setState({
...state,
showCreateWorkspaceDialog: false,
});
navigate(`/${workspace.name}`);
}}
/>
)}
</>
);
};

View File

@@ -1,136 +0,0 @@
import { useEffect } from "react";
import { toast } from "react-hot-toast";
import { deleteWorkspaceUser, upsertWorkspaceUser } from "../helpers/api";
import { useAppSelector } from "../store";
import { unknownWorkspace, unknownWorkspaceUser } from "../store/modules/workspace";
import useLoading from "../hooks/useLoading";
import { workspaceService } from "../services";
import Dropdown from "./common/Dropdown";
import { showCommonDialog } from "./Alert";
import Icon from "./Icon";
const userRoles = ["Admin", "User"];
interface Props {
workspaceId: WorkspaceId;
}
const MemberListView: React.FC<Props> = (props: Props) => {
const { workspaceId } = props;
const user = useAppSelector((state) => state.user.user) as User;
const { workspaceList } = useAppSelector((state) => state.workspace);
const workspace = workspaceList.find((workspace) => workspace.id === workspaceId) ?? unknownWorkspace;
const currentUser = workspace.workspaceUserList.find((workspaceUser) => workspaceUser.userId === user.id) ?? unknownWorkspaceUser;
const loadingState = useLoading();
useEffect(() => {
const workspace = workspaceService.getWorkspaceById(workspaceId);
if (!workspace) {
toast.error("workspace not found");
return;
}
loadingState.setFinish();
}, []);
const handleWorkspaceUserRoleChange = async (workspaceUser: WorkspaceUser, role: Role) => {
if (workspaceUser.userId === currentUser.userId) {
toast.error("Cannot change yourself.");
return;
}
await upsertWorkspaceUser({
workspaceId: workspaceId,
userId: workspaceUser.userId,
role,
});
await workspaceService.fetchWorkspaceById(workspaceId);
};
const handleDeleteWorkspaceUserButtonClick = (workspaceUser: WorkspaceUser) => {
showCommonDialog({
title: "Delete Workspace Member",
content: `Are you sure to delete member \`${workspaceUser.displayName}\` in this workspace?`,
style: "danger",
onConfirm: async () => {
await deleteWorkspaceUser({
workspaceId: workspaceId,
userId: workspaceUser.userId,
});
await workspaceService.fetchWorkspaceById(workspaceId);
},
});
};
return (
<div className="w-full flex flex-col justify-start items-start">
{loadingState.isLoading ? (
<></>
) : (
workspace.workspaceUserList.map((workspaceUser) => {
return (
<div key={workspaceUser.userId} className="w-full flex flex-row justify-between items-start border px-6 py-4 mb-3 rounded-lg">
<div className="flex flex-row justify-start items-center mr-4">
<span>{workspaceUser.displayName}</span>
{currentUser.userId === workspaceUser.userId && <span className="ml-2 text-gray-400">(yourself)</span>}
</div>
<div className="flex flex-row justify-end items-center">
{currentUser.role === "ADMIN" ? (
<>
<Dropdown
className="mr-4"
trigger={
<button className="flex flex-row justify-end items-center cursor-pointer">
<span className="font-mono">{workspaceUser.role}</span>
<Icon.ChevronDown className="ml-1 w-5 h-auto text-gray-600" />
</button>
}
actions={
<>
{userRoles.map((role) => {
return (
<button
key={role}
className="w-full px-3 leading-10 flex flex-row justify-between items-center text-left cursor-pointer rounded whitespace-nowrap hover:bg-gray-100"
onClick={() => handleWorkspaceUserRoleChange(workspaceUser, role.toUpperCase() as Role)}
>
<span className="truncate">{role}</span>
{workspaceUser.role === role.toLocaleUpperCase() && <Icon.Check className="w-4 h-auto ml-1 shrink-0" />}
</button>
);
})}
</>
}
actionsClassName="!w-28 !-left-4"
></Dropdown>
<Dropdown
actions={
<>
<button
className="w-full px-3 text-left leading-10 cursor-pointer rounded text-red-600 hover:bg-gray-100 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
onClick={() => {
handleDeleteWorkspaceUserButtonClick(workspaceUser);
}}
>
Delete
</button>
</>
}
actionsClassName="!w-24"
></Dropdown>
</>
) : (
<>
<span className="font-mono">{workspaceUser.role}</span>
</>
)}
</div>
</div>
);
})
)}
</div>
);
};
export default MemberListView;

View File

@@ -3,9 +3,8 @@ import copy from "copy-to-clipboard";
import { useState } from "react";
import { toast } from "react-hot-toast";
import { UNKNOWN_ID } from "../helpers/consts";
import { shortcutService, workspaceService } from "../services";
import { shortcutService } from "../services";
import { useAppSelector } from "../store";
import { unknownWorkspace, unknownWorkspaceUser } from "../store/modules/workspace";
import { absolutifyLink } from "../helpers/utils";
import { showCommonDialog } from "./Alert";
import Icon from "./Icon";
@@ -13,7 +12,6 @@ import Dropdown from "./common/Dropdown";
import CreateShortcutDialog from "./CreateShortcutDialog";
interface Props {
workspaceId: WorkspaceId;
shortcutList: Shortcut[];
}
@@ -22,22 +20,18 @@ interface State {
}
const ShortcutListView: React.FC<Props> = (props: Props) => {
const { workspaceId, shortcutList } = props;
const { shortcutList } = props;
const user = useAppSelector((state) => state.user.user as User);
const { workspaceList } = useAppSelector((state) => state.workspace);
const [state, setState] = useState<State>({
currentEditingShortcutId: UNKNOWN_ID,
});
const workspace = workspaceList.find((workspace) => workspace.id === workspaceId) ?? unknownWorkspace;
const workspaceUser = workspace.workspaceUserList.find((workspaceUser) => workspaceUser.userId === user.id) ?? unknownWorkspaceUser;
const havePermission = (shortcut: Shortcut) => {
return workspaceUser.role === "ADMIN" || shortcut.creatorId === user.id;
return user.role === "ADMIN" || shortcut.creatorId === user.id;
};
const handleCopyButtonClick = (shortcut: Shortcut) => {
const workspace = workspaceService.getWorkspaceById(workspaceId);
copy(absolutifyLink(`/${workspace?.name}/${shortcut.name}`));
copy(absolutifyLink(`/${shortcut.name}`));
toast.success("Shortcut link copied to clipboard.");
};
@@ -117,7 +111,6 @@ const ShortcutListView: React.FC<Props> = (props: Props) => {
{state.currentEditingShortcutId !== UNKNOWN_ID && (
<CreateShortcutDialog
workspaceId={workspaceId}
shortcutId={state.currentEditingShortcutId}
onClose={() => {
setState({

View File

@@ -1,116 +0,0 @@
import { Button, Input, Modal, ModalDialog, Radio, RadioGroup } from "@mui/joy";
import { useState } from "react";
import { toast } from "react-hot-toast";
import { workspaceService } from "../services";
import { UNKNOWN_ID } from "../helpers/consts";
import { upsertWorkspaceUser } from "../helpers/api";
import useLoading from "../hooks/useLoading";
import Icon from "./Icon";
interface Props {
workspaceId: WorkspaceId;
onClose: () => void;
onConfirm?: () => void;
}
interface State {
workspaceUserUpsert: WorkspaceUserUpsert;
}
const UpsertWorkspaceUserDialog: React.FC<Props> = (props: Props) => {
const { onClose, onConfirm, workspaceId } = props;
const [state, setState] = useState<State>({
workspaceUserUpsert: {
workspaceId: workspaceId,
userId: UNKNOWN_ID,
role: "USER",
},
});
const requestState = useLoading(false);
const handleUserIdInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const text = e.target.value as string;
setState({
workspaceUserUpsert: Object.assign(state.workspaceUserUpsert, {
userId: Number(text),
}),
});
};
const handleUserRoleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const text = e.target.value as string;
setState({
workspaceUserUpsert: Object.assign(state.workspaceUserUpsert, {
role: text,
}),
});
};
const handleSaveBtnClick = async () => {
if (!state.workspaceUserUpsert.userId) {
toast.error("User ID is required");
return;
}
requestState.setLoading();
try {
await upsertWorkspaceUser({
...state.workspaceUserUpsert,
});
await workspaceService.fetchWorkspaceById(workspaceId);
if (onConfirm) {
onConfirm();
} else {
onClose();
}
} catch (error: any) {
console.error(error);
toast.error(JSON.stringify(error.response.data));
}
requestState.setFinish();
};
return (
<Modal open={true}>
<ModalDialog>
<div className="flex flex-row justify-between items-center w-80">
<span className="text-lg font-medium">Create Workspace Member</span>
<Button variant="plain" onClick={onClose}>
<Icon.X className="w-5 h-auto text-gray-600" />
</Button>
</div>
<div>
<div className="w-full flex flex-col justify-start items-start mb-3">
<span className="mb-2">User ID</span>
<Input
className="w-full"
type="number"
value={state.workspaceUserUpsert.userId <= 0 ? "" : state.workspaceUserUpsert.userId}
onChange={handleUserIdInputChange}
/>
</div>
<div className="w-full flex flex-col justify-start items-start mb-3">
<span className="mb-2">Role</span>
<div>
<RadioGroup orientation="horizontal" value={state.workspaceUserUpsert.role} onChange={handleUserRoleInputChange}>
<Radio value="USER" label="User" />
<Radio value="ADMIN" label="Admin" />
</RadioGroup>
</div>
</div>
<div className="w-full flex flex-row justify-end items-center">
<Button color="primary" disabled={requestState.isLoading} loading={requestState.isLoading} onClick={handleSaveBtnClick}>
Save
</Button>
</div>
</div>
</ModalDialog>
</Modal>
);
};
export default UpsertWorkspaceUserDialog;

View File

@@ -1,94 +0,0 @@
import { useState } from "react";
import { Link } from "react-router-dom";
import { UNKNOWN_ID } from "../helpers/consts";
import { workspaceService } from "../services";
import { showCommonDialog } from "./Alert";
import Dropdown from "./common/Dropdown";
import CreateWorkspaceDialog from "./CreateWorkspaceDialog";
interface Props {
workspaceList: Workspace[];
}
interface State {
currentEditingWorkspaceId: WorkspaceId;
}
const WorkspaceListView: React.FC<Props> = (props: Props) => {
const { workspaceList } = props;
const [state, setState] = useState<State>({
currentEditingWorkspaceId: UNKNOWN_ID,
});
const handleEditWorkspaceButtonClick = (workspaceId: WorkspaceId) => {
setState({
...state,
currentEditingWorkspaceId: workspaceId,
});
};
const handleDeleteWorkspaceButtonClick = (workspace: Workspace) => {
showCommonDialog({
title: "Delete Workspace",
content: `Are you sure to delete workspace \`${workspace.name}\`?`,
style: "danger",
onConfirm: async () => {
await workspaceService.deleteWorkspaceById(workspace.id);
},
});
};
return (
<>
<div className="w-full flex flex-col justify-start items-start">
{workspaceList.map((workspace) => {
return (
<div key={workspace.id} className="w-full flex flex-row justify-between items-start border px-6 py-4 mb-3 rounded-lg">
<div className="flex flex-col justify-start items-start">
<Link to={`/${workspace.name}`} className="text-lg cursor-pointer hover:underline">
{workspace.name}
</Link>
<span className="text-sm mt-1 text-gray-600">{workspace.description}</span>
</div>
<Dropdown
actions={
<>
<button
className="w-full px-3 text-left leading-10 cursor-pointer rounded hover:bg-gray-100"
onClick={() => handleEditWorkspaceButtonClick(workspace.id)}
>
Edit
</button>
<button
className="w-full px-3 text-left leading-10 cursor-pointer rounded text-red-600 hover:bg-gray-100"
onClick={() => {
handleDeleteWorkspaceButtonClick(workspace);
}}
>
Delete
</button>
</>
}
actionsClassName="!w-24"
></Dropdown>
</div>
);
})}
</div>
{state.currentEditingWorkspaceId !== UNKNOWN_ID && (
<CreateWorkspaceDialog
workspaceId={state.currentEditingWorkspaceId}
onClose={() => {
setState({
...state,
currentEditingWorkspaceId: UNKNOWN_ID,
});
}}
/>
)}
</>
);
};
export default WorkspaceListView;

View File

@@ -1,189 +0,0 @@
import { Button } from "@mui/joy";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { toast } from "react-hot-toast";
import { deleteWorkspaceUser } from "../helpers/api";
import useLoading from "../hooks/useLoading";
import { workspaceService } from "../services";
import { useAppSelector } from "../store";
import { unknownWorkspace, unknownWorkspaceUser } from "../store/modules/workspace";
import { showCommonDialog } from "./Alert";
import Icon from "./Icon";
import CreateWorkspaceDialog from "./CreateWorkspaceDialog";
import UpsertWorkspaceUserDialog from "./UpsertWorkspaceUserDialog";
import MemberListView from "./MemberListView";
interface Props {
workspaceId: WorkspaceId;
}
interface State {
showEditWorkspaceDialog: boolean;
showUpsertWorkspaceUserDialog: boolean;
}
const WorkspaceSetting: React.FC<Props> = (props: Props) => {
const { workspaceId } = props;
const navigate = useNavigate();
const user = useAppSelector((state) => state.user.user) as User;
const [state, setState] = useState<State>({
showEditWorkspaceDialog: false,
showUpsertWorkspaceUserDialog: false,
});
const { workspaceList } = useAppSelector((state) => state.workspace);
const loadingState = useLoading();
const workspace = workspaceList.find((workspace) => workspace.id === workspaceId) ?? unknownWorkspace;
const workspaceUser = workspace.workspaceUserList.find((workspaceUser) => workspaceUser.userId === user.id) ?? unknownWorkspaceUser;
useEffect(() => {
const workspace = workspaceService.getWorkspaceById(workspaceId);
if (!workspace) {
toast.error("workspace not found");
return;
}
loadingState.setFinish();
}, []);
const handleEditWorkspaceButtonClick = () => {
setState({
...state,
showEditWorkspaceDialog: true,
});
};
const handleUpsertWorkspaceMemberButtonClick = () => {
setState({
...state,
showUpsertWorkspaceUserDialog: true,
});
};
const handleEditWorkspaceDialogConfirm = async () => {
setState({
...state,
showEditWorkspaceDialog: false,
});
const workspace = await workspaceService.fetchWorkspaceById(workspaceId);
navigate(`/${workspace.name}#setting`);
};
const handleDeleteWorkspaceButtonClick = () => {
showCommonDialog({
title: "Delete Workspace",
content: `Are you sure to delete workspace \`${workspace.name}\`?`,
style: "danger",
onConfirm: async () => {
await workspaceService.deleteWorkspaceById(workspace.id);
navigate("/");
},
});
};
const handleExitWorkspaceButtonClick = () => {
showCommonDialog({
title: "Exit Workspace",
content: `Are you sure to exit workspace \`${workspace.name}\`?`,
style: "danger",
onConfirm: async () => {
await deleteWorkspaceUser({
workspaceId: workspace.id,
userId: workspaceUser.userId,
});
navigate("/");
},
});
};
return (
<>
<div className="w-full flex flex-col justify-start items-start">
<span className="w-full text-2xl font-medium border-b pb-2 mb-4">General</span>
<p className="mb-4">ID: {workspace.name}</p>
<p className="mb-4">Name: {workspace.title}</p>
<p className="mb-4">Description: {workspace.description || "No description."}</p>
{workspaceUser.role === "ADMIN" && (
<div className="flex flex-row justify-start items-center">
<div className="flex flex-row justify-start items-center space-x-2">
<Button variant="soft" onClick={handleEditWorkspaceButtonClick}>
<Icon.Edit className="w-4 h-auto mr-1" />
Edit
</Button>
</div>
</div>
)}
</div>
<div className="w-full mt-8 flex flex-col justify-start items-start">
<div className="w-full border-b pb-2 mb-4 flex flex-row justify-between items-center">
<span className="text-2xl font-medium">Members</span>
{workspaceUser.role === "ADMIN" && (
<Button variant="soft" onClick={handleUpsertWorkspaceMemberButtonClick}>
<Icon.Plus className="w-4 h-auto mr-1" />
New member
</Button>
)}
</div>
<MemberListView workspaceId={workspaceId} />
</div>
<div className="w-full mt-8 flex flex-col justify-start items-start">
<span className="w-full text-2xl font-medium border-b pb-2 mb-4">Danger Zone</span>
<div className="flex flex-row justify-start items-center">
<div className="flex flex-row justify-start items-center space-x-2">
{workspaceUser.role === "ADMIN" ? (
<>
<Button variant="soft" color="danger" onClick={handleDeleteWorkspaceButtonClick}>
<Icon.Trash className="w-4 h-auto mr-1" />
Delete
</Button>
</>
) : (
<>
<Button variant="soft" color="danger" onClick={handleExitWorkspaceButtonClick}>
Exit
</Button>
</>
)}
</div>
</div>
</div>
{state.showEditWorkspaceDialog && (
<CreateWorkspaceDialog
workspaceId={workspace.id}
onClose={() => {
setState({
...state,
showEditWorkspaceDialog: false,
});
}}
onConfirm={() => handleEditWorkspaceDialogConfirm()}
/>
)}
{state.showUpsertWorkspaceUserDialog && (
<UpsertWorkspaceUserDialog
workspaceId={workspace.id}
onClose={() => {
setState({
...state,
showUpsertWorkspaceUserDialog: false,
});
}}
onConfirm={async () => {
setState({
...state,
showUpsertWorkspaceUserDialog: false,
});
if (location.hash !== "#members") {
navigate("#members");
}
}}
/>
)}
</>
);
};
export default WorkspaceSetting;