mirror of
https://github.com/aykhans/slash-e.git
synced 2025-09-06 09:14:18 +00:00
chore: update frontend modules
This commit is contained in:
@@ -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,
|
||||
|
@@ -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;
|
@@ -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}`);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -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;
|
@@ -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({
|
||||
|
@@ -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;
|
@@ -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;
|
@@ -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;
|
Reference in New Issue
Block a user