mirror of
https://github.com/aykhans/slash-e.git
synced 2025-07-07 13:42:34 +00:00
feat: install mui
This commit is contained in:
94
web/src/components/Alert.tsx
Normal file
94
web/src/components/Alert.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import { Dialog, DialogContent, DialogTitle } from "@mui/material";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import Icon from "./Icon";
|
||||
|
||||
type DialogStyle = "info" | "warning";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
content: string;
|
||||
style?: DialogStyle;
|
||||
closeBtnText?: string;
|
||||
confirmBtnText?: string;
|
||||
onClose?: () => void;
|
||||
onConfirm?: () => void;
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
title: "",
|
||||
content: "",
|
||||
style: "info",
|
||||
closeBtnText: "Close",
|
||||
confirmBtnText: "Confirm",
|
||||
onClose: () => null,
|
||||
onConfirm: () => null,
|
||||
};
|
||||
|
||||
const Alert: React.FC<Props> = (props: Props) => {
|
||||
const { title, content, closeBtnText, confirmBtnText, onClose, onConfirm, style } = {
|
||||
...defaultProps,
|
||||
...props,
|
||||
};
|
||||
|
||||
const handleCloseBtnClick = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleConfirmBtnClick = async () => {
|
||||
onConfirm();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={true}>
|
||||
<DialogTitle className="flex flex-row justify-between items-center w-80">
|
||||
<p className="text-base">{title}</p>
|
||||
<button className="rounded p-1 hover:bg-gray-100" onClick={handleCloseBtnClick}>
|
||||
<Icon.X className="w-5 h-auto text-gray-600" />
|
||||
</button>
|
||||
</DialogTitle>
|
||||
<DialogContent className="w-80">
|
||||
<p className="content-text mb-4">{content}</p>
|
||||
<div className="w-full flex flex-row justify-end items-center">
|
||||
<button className="rounded px-3 py-2 mr-2 hover:opacity-80" onClick={handleCloseBtnClick}>
|
||||
{closeBtnText}
|
||||
</button>
|
||||
<button
|
||||
className={`rounded px-3 py-2 shadow bg-green-600 text-white hover:opacity-80 ${
|
||||
style === "warning" ? "border-red-600 text-red-600 bg-red-100" : ""
|
||||
}`}
|
||||
onClick={handleConfirmBtnClick}
|
||||
>
|
||||
{confirmBtnText}
|
||||
</button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export const showCommonDialog = (props: Props) => {
|
||||
const tempDiv = document.createElement("div");
|
||||
const dialog = createRoot(tempDiv);
|
||||
document.body.append(tempDiv);
|
||||
|
||||
const destory = () => {
|
||||
dialog.unmount();
|
||||
tempDiv.remove();
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
if (props.onClose) {
|
||||
props.onClose();
|
||||
}
|
||||
destory();
|
||||
};
|
||||
|
||||
const onConfirm = () => {
|
||||
if (props.onConfirm) {
|
||||
props.onConfirm();
|
||||
}
|
||||
destory();
|
||||
};
|
||||
|
||||
dialog.render(<Alert {...props} onClose={onClose} onConfirm={onConfirm} />);
|
||||
};
|
@ -1,9 +1,9 @@
|
||||
import { Dialog, DialogContent, DialogTitle } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { validate, ValidatorConfig } from "../helpers/validator";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
import { userService } from "../services";
|
||||
import Icon from "./Icon";
|
||||
import { generateDialog } from "./Dialog";
|
||||
import toastHelper from "./Toast";
|
||||
|
||||
const validateConfig: ValidatorConfig = {
|
||||
@ -13,15 +13,18 @@ const validateConfig: ValidatorConfig = {
|
||||
noChinese: true,
|
||||
};
|
||||
|
||||
type Props = DialogProps;
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
const ChangePasswordDialog: React.FC<Props> = (props: Props) => {
|
||||
const { onClose } = props;
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
const [newPasswordAgain, setNewPasswordAgain] = useState("");
|
||||
const requestState = useLoading(false);
|
||||
|
||||
const handleCloseBtnClick = () => {
|
||||
destroy();
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleNewPasswordChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@ -59,8 +62,8 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
id: user.id,
|
||||
password: newPassword,
|
||||
});
|
||||
onClose();
|
||||
toastHelper.info("Password changed");
|
||||
handleCloseBtnClick();
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
toastHelper.error(error.response.data.message);
|
||||
@ -69,14 +72,14 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="max-w-full w-80 flex flex-row justify-between items-center mb-4">
|
||||
<Dialog open={true}>
|
||||
<DialogTitle className="flex flex-row justify-between items-center w-80">
|
||||
<p className="text-base">Change Password</p>
|
||||
<button className="rounded p-1 hover:bg-gray-100" onClick={destroy}>
|
||||
<button className="rounded p-1 hover:bg-gray-100" onClick={handleCloseBtnClick}>
|
||||
<Icon.X className="w-5 h-auto text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="w-full flex flex-col justify-start items-start">
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<div className="w-full flex flex-col justify-start items-start mb-3">
|
||||
<span className="mb-2">New Password</span>
|
||||
<input
|
||||
@ -98,8 +101,8 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
<div className="w-full flex flex-row justify-end items-center">
|
||||
<button
|
||||
disabled={requestState.isLoading}
|
||||
className={`rounded px-3 py-2 ${requestState.isLoading ? "opacity-80" : ""}`}
|
||||
onClick={destroy}
|
||||
className={`rounded px-3 py-2 mr-2 hover:opacity-80 ${requestState.isLoading ? "opacity-80" : ""}`}
|
||||
onClick={handleCloseBtnClick}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@ -111,13 +114,9 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
function showChangePasswordDialog() {
|
||||
generateDialog({}, ChangePasswordDialog);
|
||||
}
|
||||
|
||||
export default showChangePasswordDialog;
|
||||
export default ChangePasswordDialog;
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { Dialog, DialogContent, DialogTitle } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { shortcutService } from "../services";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
import Icon from "./Icon";
|
||||
import { generateDialog } from "./Dialog";
|
||||
import toastHelper from "./Toast";
|
||||
|
||||
interface Props extends DialogProps {
|
||||
interface Props {
|
||||
workspaceId: WorkspaceId;
|
||||
shortcutId?: ShortcutId;
|
||||
onClose: () => void;
|
||||
onConfirm?: () => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@ -15,7 +17,7 @@ interface State {
|
||||
}
|
||||
|
||||
const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
const { destroy, workspaceId, shortcutId } = props;
|
||||
const { onClose, onConfirm, workspaceId, shortcutId } = props;
|
||||
const [state, setState] = useState<State>({
|
||||
shortcutCreate: {
|
||||
workspaceId: workspaceId,
|
||||
@ -90,7 +92,12 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
} else {
|
||||
await shortcutService.createShortcut(state.shortcutCreate);
|
||||
}
|
||||
destroy();
|
||||
|
||||
if (onConfirm) {
|
||||
onConfirm();
|
||||
} else {
|
||||
onClose();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
toastHelper.error(error.response.data.error || error.response.data.message);
|
||||
@ -98,14 +105,14 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="max-w-full w-80 sm:w-96 flex flex-row justify-between items-center mb-4">
|
||||
<Dialog open={true}>
|
||||
<DialogTitle className="flex flex-row justify-between items-center w-80 sm:w-96">
|
||||
<p className="text-base">{shortcutId ? "Edit Shortcut" : "Create Shortcut"}</p>
|
||||
<button className="rounded p-1 hover:bg-gray-100" onClick={destroy}>
|
||||
<button className="rounded p-1 hover:bg-gray-100" onClick={onClose}>
|
||||
<Icon.X className="w-5 h-auto text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="w-full flex flex-col justify-start items-start">
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<div className="w-full flex flex-col justify-start items-start mb-3">
|
||||
<span className="mb-2">Name</span>
|
||||
<input
|
||||
@ -184,20 +191,9 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default function showCreateShortcutDialog(workspaceId: WorkspaceId, shortcutId?: ShortcutId, onDestory?: () => void): void {
|
||||
generateDialog(
|
||||
{
|
||||
onDestory,
|
||||
},
|
||||
CreateShortcutDialog,
|
||||
{
|
||||
workspaceId,
|
||||
shortcutId,
|
||||
}
|
||||
);
|
||||
}
|
||||
export default CreateShortcutDialog;
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { Dialog, DialogContent, DialogTitle } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { workspaceService } from "../services";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
import Icon from "./Icon";
|
||||
import { generateDialog } from "./Dialog";
|
||||
import toastHelper from "./Toast";
|
||||
|
||||
interface Props extends DialogProps {
|
||||
interface Props {
|
||||
workspaceId?: WorkspaceId;
|
||||
onClose: () => void;
|
||||
onConfirm?: () => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@ -14,7 +16,7 @@ interface State {
|
||||
}
|
||||
|
||||
const CreateWorkspaceDialog: React.FC<Props> = (props: Props) => {
|
||||
const { destroy, workspaceId } = props;
|
||||
const { onClose, onConfirm, workspaceId } = props;
|
||||
const [state, setState] = useState<State>({
|
||||
workspaceCreate: {
|
||||
name: "",
|
||||
@ -75,7 +77,12 @@ const CreateWorkspaceDialog: React.FC<Props> = (props: Props) => {
|
||||
...state.workspaceCreate,
|
||||
});
|
||||
}
|
||||
destroy();
|
||||
|
||||
if (onConfirm) {
|
||||
onConfirm();
|
||||
} else {
|
||||
onClose();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
toastHelper.error(error.response.data.error || error.response.data.message);
|
||||
@ -84,14 +91,14 @@ const CreateWorkspaceDialog: React.FC<Props> = (props: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="max-w-full w-80 flex flex-row justify-between items-center mb-4">
|
||||
<Dialog open={true}>
|
||||
<DialogTitle className="flex flex-row justify-between items-center w-80">
|
||||
<p className="text-base">{workspaceId ? "Edit Workspace" : "Create Workspace"}</p>
|
||||
<button className="rounded p-1 hover:bg-gray-100" onClick={destroy}>
|
||||
<button className="rounded p-1 hover:bg-gray-100" onClick={onClose}>
|
||||
<Icon.X className="w-5 h-auto text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="w-full flex flex-col justify-start items-start">
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<div className="w-full flex flex-col justify-start items-start mb-3">
|
||||
<span className="mb-2">Name</span>
|
||||
<input
|
||||
@ -121,13 +128,9 @@ const CreateWorkspaceDialog: React.FC<Props> = (props: Props) => {
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default function showCreateWorkspaceDialog(workspaceId?: WorkspaceId): void {
|
||||
generateDialog({}, CreateWorkspaceDialog, {
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
export default CreateWorkspaceDialog;
|
||||
|
@ -1,94 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { Provider } from "react-redux";
|
||||
import { ANIMATION_DURATION } from "../../helpers/consts";
|
||||
import store from "../../store";
|
||||
import "../../less/base-dialog.less";
|
||||
|
||||
interface DialogConfig {
|
||||
className?: string;
|
||||
clickSpaceDestroy?: boolean;
|
||||
onDestory?: () => void;
|
||||
}
|
||||
|
||||
interface Props extends DialogConfig, DialogProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const BaseDialog: React.FC<Props> = (props: Props) => {
|
||||
const { children, className, clickSpaceDestroy, destroy } = props;
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.code === "Escape") {
|
||||
destroy();
|
||||
}
|
||||
};
|
||||
|
||||
document.body.addEventListener("keydown", handleKeyDown);
|
||||
|
||||
return () => {
|
||||
document.body.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleSpaceClicked = () => {
|
||||
if (clickSpaceDestroy) {
|
||||
destroy();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`dialog-wrapper px-2 sm:px-0 ${className}`} onClick={handleSpaceClicked}>
|
||||
<div className="dialog-container" onClick={(e) => e.stopPropagation()}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export function generateDialog<T extends DialogProps>(
|
||||
config: DialogConfig,
|
||||
DialogComponent: React.FC<T>,
|
||||
props?: Omit<T, "destroy">
|
||||
): DialogCallback {
|
||||
const tempDiv = document.createElement("div");
|
||||
const dialog = createRoot(tempDiv);
|
||||
document.body.append(tempDiv);
|
||||
|
||||
setTimeout(() => {
|
||||
tempDiv.firstElementChild?.classList.add("showup");
|
||||
}, 0);
|
||||
|
||||
const cbs: DialogCallback = {
|
||||
destroy: () => {
|
||||
tempDiv.firstElementChild?.classList.remove("showup");
|
||||
tempDiv.firstElementChild?.classList.add("showoff");
|
||||
setTimeout(() => {
|
||||
dialog.unmount();
|
||||
tempDiv.remove();
|
||||
}, ANIMATION_DURATION);
|
||||
|
||||
if (config.onDestory) {
|
||||
config.onDestory();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const dialogProps = {
|
||||
...props,
|
||||
destroy: cbs.destroy,
|
||||
} as T;
|
||||
|
||||
const Fragment = (
|
||||
<Provider store={store}>
|
||||
<BaseDialog destroy={cbs.destroy} clickSpaceDestroy={true} {...config}>
|
||||
<DialogComponent {...dialogProps} />
|
||||
</BaseDialog>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
dialog.render(Fragment);
|
||||
|
||||
return cbs;
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
import Icon from "../Icon";
|
||||
import { generateDialog } from "./BaseDialog";
|
||||
import "../../less/common-dialog.less";
|
||||
|
||||
type DialogStyle = "info" | "warning";
|
||||
|
||||
interface Props extends DialogProps {
|
||||
title: string;
|
||||
content: string;
|
||||
style?: DialogStyle;
|
||||
closeBtnText?: string;
|
||||
confirmBtnText?: string;
|
||||
onClose?: () => void;
|
||||
onConfirm?: () => void;
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
title: "",
|
||||
content: "",
|
||||
style: "info",
|
||||
closeBtnText: "Close",
|
||||
confirmBtnText: "Confirm",
|
||||
onClose: () => null,
|
||||
onConfirm: () => null,
|
||||
};
|
||||
|
||||
const CommonDialog: React.FC<Props> = (props: Props) => {
|
||||
const { title, content, destroy, closeBtnText, confirmBtnText, onClose, onConfirm, style } = {
|
||||
...defaultProps,
|
||||
...props,
|
||||
};
|
||||
|
||||
const handleCloseBtnClick = () => {
|
||||
onClose();
|
||||
destroy();
|
||||
};
|
||||
|
||||
const handleConfirmBtnClick = async () => {
|
||||
onConfirm();
|
||||
destroy();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="dialog-header-container">
|
||||
<p className="title-text">{title}</p>
|
||||
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
||||
<Icon.X />
|
||||
</button>
|
||||
</div>
|
||||
<div className="dialog-content-container">
|
||||
<p className="content-text">{content}</p>
|
||||
<div className="btns-container">
|
||||
<span className="btn cancel-btn" onClick={handleCloseBtnClick}>
|
||||
{closeBtnText}
|
||||
</span>
|
||||
<span className={`btn confirm-btn ${style}`} onClick={handleConfirmBtnClick}>
|
||||
{confirmBtnText}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface CommonDialogProps {
|
||||
title: string;
|
||||
content: string;
|
||||
className?: string;
|
||||
style?: DialogStyle;
|
||||
closeBtnText?: string;
|
||||
confirmBtnText?: string;
|
||||
onClose?: () => void;
|
||||
onConfirm?: () => void;
|
||||
}
|
||||
|
||||
export const showCommonDialog = (props: CommonDialogProps) => {
|
||||
generateDialog(
|
||||
{
|
||||
className: `common-dialog ${props?.className ?? ""}`,
|
||||
},
|
||||
CommonDialog,
|
||||
props
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export { generateDialog } from "./BaseDialog";
|
@ -1,102 +1,130 @@
|
||||
import { useState } from "react";
|
||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||
import { useAppSelector } from "../store";
|
||||
import { userService } from "../services";
|
||||
import Icon from "./Icon";
|
||||
import Dropdown from "./common/Dropdown";
|
||||
import showCreateWorkspaceDialog from "./CreateWorkspaceDialog";
|
||||
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();
|
||||
navigate("/user/auth");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full bg-amber-50">
|
||||
<div className="w-full max-w-4xl mx-auto px-3 py-5 flex flex-row justify-between items-center">
|
||||
<div className="flex flex-row justify-start items-center">
|
||||
<Link to={"/"} className="text-base font-mono font-medium cursor-pointer">
|
||||
Corgi
|
||||
</Link>
|
||||
{workspaceList.length > 0 && activedWorkspace !== undefined && (
|
||||
<>
|
||||
<span className="font-mono mx-2 text-gray-200">/</span>
|
||||
<>
|
||||
<div className="w-full bg-amber-50">
|
||||
<div className="w-full max-w-4xl mx-auto px-3 py-5 flex flex-row justify-between items-center">
|
||||
<div className="flex flex-row justify-start items-center">
|
||||
<Link to={"/"} className="text-base font-mono font-medium cursor-pointer">
|
||||
Corgi
|
||||
</Link>
|
||||
{workspaceList.length > 0 && activedWorkspace !== undefined && (
|
||||
<>
|
||||
<span className="font-mono mx-2 text-gray-200">/</span>
|
||||
<Dropdown
|
||||
trigger={
|
||||
<button className="flex flex-row justify-end items-center cursor-pointer">
|
||||
<span className="font-mono">{activedWorkspace?.name}</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.name}</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">
|
||||
{user ? (
|
||||
<Dropdown
|
||||
trigger={
|
||||
<button className="flex flex-row justify-end items-center cursor-pointer">
|
||||
<span className="font-mono">{activedWorkspace?.name}</span>
|
||||
<span>{user?.name}</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.name}</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={() => showCreateWorkspaceDialog()}
|
||||
<Link
|
||||
to="/account"
|
||||
className="w-full flex flex-row justify-start items-center px-3 leading-10 text-left cursor-pointer rounded whitespace-nowrap hover:bg-gray-100"
|
||||
>
|
||||
<Icon.Plus className="w-4 h-auto mr-1" /> Create Workspace
|
||||
<Icon.User className="w-4 h-auto mr-2" /> My Account
|
||||
</Link>
|
||||
<button
|
||||
className="w-full flex flex-row justify-start items-center px-3 leading-10 text-left cursor-pointer rounded whitespace-nowrap hover:bg-gray-100"
|
||||
onClick={() => handleSignOutButtonClick()}
|
||||
>
|
||||
<Icon.LogOut className="w-4 h-auto mr-2" /> Sign out
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
actionsClassName="!w-48 !-left-4"
|
||||
actionsClassName="!w-40"
|
||||
></Dropdown>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative">
|
||||
{user ? (
|
||||
<Dropdown
|
||||
trigger={
|
||||
<button className="flex flex-row justify-end items-center cursor-pointer">
|
||||
<span>{user?.name}</span>
|
||||
<Icon.ChevronDown className="ml-1 w-5 h-auto text-gray-600" />
|
||||
</button>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<Link
|
||||
to="/account"
|
||||
className="w-full flex flex-row justify-start items-center px-3 leading-10 text-left cursor-pointer rounded whitespace-nowrap hover:bg-gray-100"
|
||||
>
|
||||
<Icon.User className="w-4 h-auto mr-1" /> My Account
|
||||
</Link>
|
||||
<button
|
||||
className="w-full flex flex-row justify-start items-center px-3 leading-10 text-left cursor-pointer rounded whitespace-nowrap hover:bg-gray-100"
|
||||
onClick={() => handleSignOutButtonClick()}
|
||||
>
|
||||
<Icon.LogOut className="w-4 h-auto mr-1" /> Sign out
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
actionsClassName="!w-40"
|
||||
></Dropdown>
|
||||
) : (
|
||||
<span className="cursor-pointer" onClick={() => navigate("/user/auth")}>
|
||||
Sign in
|
||||
</span>
|
||||
)}
|
||||
) : (
|
||||
<span className="cursor-pointer" onClick={() => navigate("/user/auth")}>
|
||||
Sign in
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{state.showCreateWorkspaceDialog && (
|
||||
<CreateWorkspaceDialog
|
||||
onClose={() => {
|
||||
setState({
|
||||
...state,
|
||||
showCreateWorkspaceDialog: false,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -4,7 +4,7 @@ import useLoading from "../hooks/useLoading";
|
||||
import { workspaceService } from "../services";
|
||||
import toastHelper from "./Toast";
|
||||
import Dropdown from "./common/Dropdown";
|
||||
import { showCommonDialog } from "./Dialog/CommonDialog";
|
||||
import { showCommonDialog } from "./Alert";
|
||||
import Icon from "./Icon";
|
||||
|
||||
const userRoles = ["Admin", "User"];
|
||||
@ -26,17 +26,16 @@ const MemberListView: React.FC<Props> = (props: Props) => {
|
||||
});
|
||||
const loadingState = useLoading();
|
||||
|
||||
const fetchWorkspaceUserList = () => {
|
||||
const fetchWorkspaceUserList = async () => {
|
||||
loadingState.setLoading();
|
||||
return Promise.all([workspaceService.getWorkspaceUserList(workspaceId)])
|
||||
.then(([workspaceUserList]) => {
|
||||
setState({
|
||||
workspaceUserList: workspaceUserList,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
loadingState.setFinish();
|
||||
try {
|
||||
const [workspaceUserList] = await Promise.all([workspaceService.getWorkspaceUserList(workspaceId)]);
|
||||
setState({
|
||||
workspaceUserList: workspaceUserList,
|
||||
});
|
||||
} finally {
|
||||
loadingState.setFinish();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -1,25 +1,41 @@
|
||||
import copy from "copy-to-clipboard";
|
||||
import { useState } from "react";
|
||||
import { shortcutService, workspaceService } from "../services";
|
||||
import { useAppSelector } from "../store";
|
||||
import { showCommonDialog } from "./Dialog/CommonDialog";
|
||||
import Dropdown from "./common/Dropdown";
|
||||
import { UNKNOWN_ID } from "../helpers/consts";
|
||||
import { showCommonDialog } from "./Alert";
|
||||
import Icon from "./Icon";
|
||||
import showCreateShortcutDialog from "./CreateShortcutDialog";
|
||||
import Dropdown from "./common/Dropdown";
|
||||
import CreateShortcutDialog from "./CreateShortcutDialog";
|
||||
|
||||
interface Props {
|
||||
workspaceId: WorkspaceId;
|
||||
shortcutList: Shortcut[];
|
||||
}
|
||||
|
||||
interface State {
|
||||
currentEditingShortcutId: ShortcutId;
|
||||
}
|
||||
|
||||
const ShortcutListView: React.FC<Props> = (props: Props) => {
|
||||
const { workspaceId, shortcutList } = props;
|
||||
const { user } = useAppSelector((state) => state.user);
|
||||
const [state, setState] = useState<State>({
|
||||
currentEditingShortcutId: UNKNOWN_ID,
|
||||
});
|
||||
|
||||
const handleCopyButtonClick = (shortcut: Shortcut) => {
|
||||
const workspace = workspaceService.getWorkspaceById(workspaceId);
|
||||
copy(`${location.host}/${workspace?.name}/go/${shortcut.name}`);
|
||||
};
|
||||
|
||||
const handleEditShortcutButtonClick = (shortcut: Shortcut) => {
|
||||
setState({
|
||||
...state,
|
||||
currentEditingShortcutId: shortcut.id,
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteShortcutButtonClick = (shortcut: Shortcut) => {
|
||||
showCommonDialog({
|
||||
title: "Delete Shortcut",
|
||||
@ -32,55 +48,76 @@ const ShortcutListView: React.FC<Props> = (props: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col justify-start items-start">
|
||||
{shortcutList.map((shortcut) => {
|
||||
return (
|
||||
<div key={shortcut.id} 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>{shortcut.name}</span>
|
||||
<span className="text-gray-400 text-sm ml-2">({shortcut.description})</span>
|
||||
<>
|
||||
<div className="w-full flex flex-col justify-start items-start">
|
||||
{shortcutList.map((shortcut) => {
|
||||
return (
|
||||
<div key={shortcut.id} 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>{shortcut.name}</span>
|
||||
<span className="text-gray-400 text-sm ml-2">({shortcut.description})</span>
|
||||
</div>
|
||||
<div className="flex flex-row justify-end items-center">
|
||||
<span className=" w-12 mr-2 text-gray-600">{shortcut.creator.name}</span>
|
||||
<button
|
||||
className="cursor-pointer mr-4 hover:opacity-80"
|
||||
onClick={() => {
|
||||
handleCopyButtonClick(shortcut);
|
||||
}}
|
||||
>
|
||||
<Icon.Copy className="w-5 h-auto" />
|
||||
</button>
|
||||
<a className="cursor-pointer mr-4 hover:opacity-80" target="blank" href={shortcut.link}>
|
||||
<Icon.ExternalLink className="w-5 h-auto" />
|
||||
</a>
|
||||
<Dropdown
|
||||
actions={
|
||||
<>
|
||||
<button
|
||||
disabled={shortcut.creatorId !== user?.id}
|
||||
className="w-full px-3 text-left leading-10 cursor-pointer rounded hover:bg-gray-100 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
|
||||
onClick={() => handleEditShortcutButtonClick(shortcut)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
disabled={shortcut.creatorId !== user?.id}
|
||||
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={() => {
|
||||
handleDeleteShortcutButtonClick(shortcut);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
actionsClassName="!w-24"
|
||||
></Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-end items-center">
|
||||
<span className=" w-12 mr-2 text-gray-600">{shortcut.creator.name}</span>
|
||||
<button
|
||||
className="cursor-pointer mr-4 hover:opacity-80"
|
||||
onClick={() => {
|
||||
handleCopyButtonClick(shortcut);
|
||||
}}
|
||||
>
|
||||
<Icon.Copy className="w-5 h-auto" />
|
||||
</button>
|
||||
<a className="cursor-pointer mr-4 hover:opacity-80" target="blank" href={shortcut.link}>
|
||||
<Icon.ExternalLink className="w-5 h-auto" />
|
||||
</a>
|
||||
<Dropdown
|
||||
actions={
|
||||
<>
|
||||
<button
|
||||
disabled={shortcut.creatorId !== user?.id}
|
||||
className="w-full px-3 text-left leading-10 cursor-pointer rounded hover:bg-gray-100 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
|
||||
onClick={() => showCreateShortcutDialog(workspaceId, shortcut.id)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
disabled={shortcut.creatorId !== user?.id}
|
||||
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={() => {
|
||||
handleDeleteShortcutButtonClick(shortcut);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
actionsClassName="!w-24"
|
||||
></Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{state.currentEditingShortcutId !== UNKNOWN_ID && (
|
||||
<CreateShortcutDialog
|
||||
workspaceId={workspaceId}
|
||||
shortcutId={state.currentEditingShortcutId}
|
||||
onClose={() => {
|
||||
setState({
|
||||
...state,
|
||||
currentEditingShortcutId: UNKNOWN_ID,
|
||||
});
|
||||
}}
|
||||
onConfirm={() => {
|
||||
setState({
|
||||
...state,
|
||||
currentEditingShortcutId: UNKNOWN_ID,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { Dialog, DialogContent, DialogTitle } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { UNKNOWN_ID } from "../helpers/consts";
|
||||
import { upsertWorkspaceUser } from "../helpers/api";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
import Icon from "./Icon";
|
||||
import { generateDialog } from "./Dialog";
|
||||
import toastHelper from "./Toast";
|
||||
|
||||
interface Props extends DialogProps {
|
||||
interface Props {
|
||||
workspaceId: WorkspaceId;
|
||||
onClose: () => void;
|
||||
onConfirm?: () => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@ -15,7 +17,7 @@ interface State {
|
||||
}
|
||||
|
||||
const UpsertWorkspaceUserDialog: React.FC<Props> = (props: Props) => {
|
||||
const { destroy, workspaceId } = props;
|
||||
const { onClose, onConfirm, workspaceId } = props;
|
||||
const [state, setState] = useState<State>({
|
||||
workspaceUserUpsert: {
|
||||
workspaceId: workspaceId,
|
||||
@ -56,7 +58,12 @@ const UpsertWorkspaceUserDialog: React.FC<Props> = (props: Props) => {
|
||||
await upsertWorkspaceUser({
|
||||
...state.workspaceUserUpsert,
|
||||
});
|
||||
destroy();
|
||||
|
||||
if (onConfirm) {
|
||||
onConfirm();
|
||||
} else {
|
||||
onClose();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
toastHelper.error(error.response.data.error || error.response.data.message);
|
||||
@ -65,14 +72,14 @@ const UpsertWorkspaceUserDialog: React.FC<Props> = (props: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="max-w-full w-80 flex flex-row justify-between items-center mb-4">
|
||||
<Dialog open={true}>
|
||||
<DialogTitle className="flex flex-row justify-between items-center w-80">
|
||||
<p className="text-base">Create Workspace Member</p>
|
||||
<button className="rounded p-1 hover:bg-gray-100" onClick={destroy}>
|
||||
<button className="rounded p-1 hover:bg-gray-100" onClick={onClose}>
|
||||
<Icon.X className="w-5 h-auto text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="w-full flex flex-col justify-start items-start">
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<div className="w-full flex flex-col justify-start items-start mb-3">
|
||||
<span className="mb-2">User ID</span>
|
||||
<input
|
||||
@ -120,19 +127,9 @@ const UpsertWorkspaceUserDialog: React.FC<Props> = (props: Props) => {
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default function showUpsertWorkspaceUserDialog(workspaceId: WorkspaceId, onDestory?: () => void) {
|
||||
return generateDialog(
|
||||
{
|
||||
onDestory,
|
||||
},
|
||||
UpsertWorkspaceUserDialog,
|
||||
{
|
||||
workspaceId,
|
||||
}
|
||||
);
|
||||
}
|
||||
export default UpsertWorkspaceUserDialog;
|
||||
|
@ -1,15 +1,31 @@
|
||||
import { useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { UNKNOWN_ID } from "../helpers/consts";
|
||||
import { workspaceService } from "../services";
|
||||
import { showCommonDialog } from "./Dialog/CommonDialog";
|
||||
import { showCommonDialog } from "./Alert";
|
||||
import Dropdown from "./common/Dropdown";
|
||||
import showCreateWorkspaceDialog from "./CreateWorkspaceDialog";
|
||||
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({
|
||||
@ -23,41 +39,55 @@ const WorkspaceListView: React.FC<Props> = (props: Props) => {
|
||||
};
|
||||
|
||||
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 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>
|
||||
<Dropdown
|
||||
actions={
|
||||
<>
|
||||
<button
|
||||
className="w-full px-3 text-left leading-10 cursor-pointer rounded hover:bg-gray-100"
|
||||
onClick={() => showCreateWorkspaceDialog(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>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{state.currentEditingWorkspaceId !== UNKNOWN_ID && (
|
||||
<CreateWorkspaceDialog
|
||||
workspaceId={state.currentEditingWorkspaceId}
|
||||
onClose={() => {
|
||||
setState({
|
||||
...state,
|
||||
currentEditingWorkspaceId: UNKNOWN_ID,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -5,9 +5,9 @@ import useLoading from "../hooks/useLoading";
|
||||
import { workspaceService } from "../services";
|
||||
import { useAppSelector } from "../store";
|
||||
import { unknownWorkspace, unknownWorkspaceUser } from "../store/modules/workspace";
|
||||
import showCreateWorkspaceDialog from "./CreateWorkspaceDialog";
|
||||
import { showCommonDialog } from "./Dialog/CommonDialog";
|
||||
import { showCommonDialog } from "./Alert";
|
||||
import toastHelper from "./Toast";
|
||||
import CreateWorkspaceDialog from "./CreateWorkspaceDialog";
|
||||
|
||||
interface Props {
|
||||
workspaceId: WorkspaceId;
|
||||
@ -16,6 +16,7 @@ interface Props {
|
||||
interface State {
|
||||
workspace: Workspace;
|
||||
workspaceUser: WorkspaceUser;
|
||||
showEditWorkspaceDialog: boolean;
|
||||
}
|
||||
|
||||
const WorkspaceSetting: React.FC<Props> = (props: Props) => {
|
||||
@ -25,6 +26,7 @@ const WorkspaceSetting: React.FC<Props> = (props: Props) => {
|
||||
const [state, setState] = useState<State>({
|
||||
workspace: unknownWorkspace,
|
||||
workspaceUser: unknownWorkspaceUser,
|
||||
showEditWorkspaceDialog: false,
|
||||
});
|
||||
const loadingState = useLoading();
|
||||
|
||||
@ -39,6 +41,7 @@ const WorkspaceSetting: React.FC<Props> = (props: Props) => {
|
||||
Promise.all([workspaceService.getWorkspaceUser(workspace.id, user.id)])
|
||||
.then(([workspaceUser]) => {
|
||||
setState({
|
||||
...state,
|
||||
workspace,
|
||||
workspaceUser,
|
||||
});
|
||||
@ -49,7 +52,29 @@ const WorkspaceSetting: React.FC<Props> = (props: Props) => {
|
||||
}, []);
|
||||
|
||||
const handleEditWorkspaceButtonClick = () => {
|
||||
showCreateWorkspaceDialog(state.workspace.id);
|
||||
setState({
|
||||
...state,
|
||||
showEditWorkspaceDialog: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleEditWorkspaceDialogConfirm = () => {
|
||||
const prevWorkspace = state.workspace;
|
||||
const workspace = workspaceService.getWorkspaceById(workspaceId);
|
||||
if (!workspace) {
|
||||
toastHelper.error("workspace not found");
|
||||
return;
|
||||
}
|
||||
|
||||
setState({
|
||||
...state,
|
||||
workspace: workspace,
|
||||
showEditWorkspaceDialog: false,
|
||||
});
|
||||
|
||||
if (prevWorkspace.name !== workspace.name) {
|
||||
navigate(`/${workspace.name}#setting`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteWorkspaceButtonClick = () => {
|
||||
@ -80,38 +105,53 @@ const WorkspaceSetting: React.FC<Props> = (props: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col justify-start items-start">
|
||||
<p className="text-3xl mt-2 mb-4">{state.workspace.name}</p>
|
||||
<p>{state.workspace.description}</p>
|
||||
<>
|
||||
<div className="w-full flex flex-col justify-start items-start">
|
||||
<p className="text-3xl mt-2 mb-4">{state.workspace.name}</p>
|
||||
<p>{state.workspace.description}</p>
|
||||
|
||||
<div className="border-t pt-4 mt-2 flex flex-row justify-start items-center">
|
||||
<span className="text-gray-400 mr-2">Actions:</span>
|
||||
<div className="flex flex-row justify-start items-center space-x-2">
|
||||
{state.workspaceUser.role === "ADMIN" ? (
|
||||
<>
|
||||
<button className="border rounded-md px-3 leading-8 hover:shadow" onClick={handleEditWorkspaceButtonClick}>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
className="border rounded-md px-3 leading-8 border-red-600 text-red-600 bg-red-50 hover:shadow"
|
||||
onClick={handleDeleteWorkspaceButtonClick}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
className="border rounded-md px-3 leading-8 border-red-600 text-red-600 bg-red-50 hover:shadow"
|
||||
onClick={handleExitWorkspaceButtonClick}
|
||||
>
|
||||
Exit
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
<div className="border-t pt-4 mt-2 flex flex-row justify-start items-center">
|
||||
<span className="text-gray-400 mr-2">Actions:</span>
|
||||
<div className="flex flex-row justify-start items-center space-x-2">
|
||||
{state.workspaceUser.role === "ADMIN" ? (
|
||||
<>
|
||||
<button className="border rounded-md px-3 leading-8 hover:shadow" onClick={handleEditWorkspaceButtonClick}>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
className="border rounded-md px-3 leading-8 border-red-600 text-red-600 bg-red-50 hover:shadow"
|
||||
onClick={handleDeleteWorkspaceButtonClick}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
className="border rounded-md px-3 leading-8 border-red-600 text-red-600 bg-red-50 hover:shadow"
|
||||
onClick={handleExitWorkspaceButtonClick}
|
||||
>
|
||||
Exit
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{state.showEditWorkspaceDialog && (
|
||||
<CreateWorkspaceDialog
|
||||
workspaceId={state.workspace.id}
|
||||
onClose={() => {
|
||||
setState({
|
||||
...state,
|
||||
showEditWorkspaceDialog: false,
|
||||
});
|
||||
}}
|
||||
onConfirm={handleEditWorkspaceDialogConfirm}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,37 +0,0 @@
|
||||
.dialog-wrapper {
|
||||
@apply fixed top-0 left-0 flex flex-col justify-start items-center w-full h-full pt-16 z-100 overflow-x-hidden overflow-y-scroll bg-transparent transition-all;
|
||||
|
||||
&.showup {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
&.showoff {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .dialog-container {
|
||||
@apply flex flex-col justify-start items-start bg-white p-4 sm:px-6 rounded-lg;
|
||||
|
||||
> .dialog-header-container {
|
||||
@apply flex flex-row justify-between items-center w-full mb-4;
|
||||
|
||||
> .title-text {
|
||||
> .icon-text {
|
||||
@apply mr-2 text-base;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
@apply flex flex-col justify-center items-center w-6 h-6 rounded hover:bg-gray-100 hover:shadow;
|
||||
}
|
||||
}
|
||||
|
||||
> .dialog-content-container {
|
||||
@apply flex flex-col justify-start items-start w-full;
|
||||
}
|
||||
|
||||
> .dialog-footer-container {
|
||||
@apply flex flex-row justify-end items-center w-full mt-4;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
.common-dialog {
|
||||
> .dialog-container {
|
||||
@apply w-80;
|
||||
|
||||
> .dialog-content-container {
|
||||
@apply flex flex-col justify-start items-start;
|
||||
|
||||
> .btns-container {
|
||||
@apply flex flex-row justify-end items-center w-full mt-4;
|
||||
|
||||
> .btn {
|
||||
@apply text-sm py-1 px-3 mr-2 rounded-md cursor-pointer hover:opacity-80;
|
||||
|
||||
&.confirm-btn {
|
||||
@apply bg-red-100 border border-solid border-blue-600 text-blue-600;
|
||||
|
||||
&.warning {
|
||||
@apply border-red-600 text-red-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { userService, workspaceService } from "../services";
|
||||
import { useAppSelector } from "../store";
|
||||
@ -6,11 +6,18 @@ import useLoading from "../hooks/useLoading";
|
||||
import Icon from "../components/Icon";
|
||||
import Header from "../components/Header";
|
||||
import WorkspaceListView from "../components/WorkspaceListView";
|
||||
import showCreateWorkspaceDialog from "../components/CreateWorkspaceDialog";
|
||||
import CreateWorkspaceDialog from "../components/CreateWorkspaceDialog";
|
||||
|
||||
interface State {
|
||||
showCreateWorkspaceDialog: boolean;
|
||||
}
|
||||
|
||||
const Home: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { workspaceList } = useAppSelector((state) => state.workspace);
|
||||
const [state, setState] = useState<State>({
|
||||
showCreateWorkspaceDialog: false,
|
||||
});
|
||||
const loadingState = useLoading();
|
||||
|
||||
useEffect(() => {
|
||||
@ -24,29 +31,49 @@ const Home: React.FC = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleCreateWorkspaceButtonClick = () => {
|
||||
setState({
|
||||
...state,
|
||||
showCreateWorkspaceDialog: true,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col justify-start items-start">
|
||||
<Header />
|
||||
<div className="mx-auto max-w-4xl w-full px-3 py-6 flex flex-col justify-start items-start">
|
||||
<div className="mb-4 w-full flex flex-row justify-between items-center">
|
||||
<span className="font-mono text-gray-400">Workspace List</span>
|
||||
<button
|
||||
className="text-sm flex flex-row justify-start items-center border px-3 leading-10 rounded-lg cursor-pointer hover:shadow"
|
||||
onClick={() => showCreateWorkspaceDialog()}
|
||||
>
|
||||
<Icon.Plus className="w-5 h-auto mr-1" /> Create Workspace
|
||||
</button>
|
||||
</div>
|
||||
{loadingState.isLoading ? (
|
||||
<div className="py-4 w-full flex flex-row justify-center items-center">
|
||||
<Icon.Loader className="mr-2 w-5 h-auto animate-spin" />
|
||||
loading
|
||||
<>
|
||||
<div className="w-full h-full flex flex-col justify-start items-start">
|
||||
<Header />
|
||||
<div className="mx-auto max-w-4xl w-full px-3 py-6 flex flex-col justify-start items-start">
|
||||
<div className="mb-4 w-full flex flex-row justify-between items-center">
|
||||
<span className="font-mono text-gray-400">Workspace List</span>
|
||||
<button
|
||||
className="text-sm flex flex-row justify-start items-center border px-3 leading-10 rounded-lg cursor-pointer hover:shadow"
|
||||
onClick={handleCreateWorkspaceButtonClick}
|
||||
>
|
||||
<Icon.Plus className="w-5 h-auto mr-1" /> Create Workspace
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<WorkspaceListView workspaceList={workspaceList} />
|
||||
)}
|
||||
{loadingState.isLoading ? (
|
||||
<div className="py-4 w-full flex flex-row justify-center items-center">
|
||||
<Icon.Loader className="mr-2 w-5 h-auto animate-spin" />
|
||||
loading
|
||||
</div>
|
||||
) : (
|
||||
<WorkspaceListView workspaceList={workspaceList} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{state.showCreateWorkspaceDialog && (
|
||||
<CreateWorkspaceDialog
|
||||
onClose={() => {
|
||||
setState({
|
||||
...state,
|
||||
showCreateWorkspaceDialog: false,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,17 +1,24 @@
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useAppSelector } from "../store";
|
||||
import Header from "../components/Header";
|
||||
import { showCommonDialog } from "../components/Dialog/CommonDialog";
|
||||
import { showCommonDialog } from "../components/Alert";
|
||||
import { userService } from "../services";
|
||||
import Icon from "../components/Icon";
|
||||
import copy from "copy-to-clipboard";
|
||||
import toastHelper from "../components/Toast";
|
||||
import showChangePasswordDialog from "../components/ChangePasswordDialog";
|
||||
import ChangePasswordDialog from "../components/ChangePasswordDialog";
|
||||
|
||||
interface State {
|
||||
showChangePasswordDialog: boolean;
|
||||
}
|
||||
|
||||
const UserDetail: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAppSelector((state) => state.user);
|
||||
const [state, setState] = useState<State>({
|
||||
showChangePasswordDialog: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!userService.getState().user) {
|
||||
@ -21,7 +28,10 @@ const UserDetail: React.FC = () => {
|
||||
}, []);
|
||||
|
||||
const handleChangePasswordBtnClick = async () => {
|
||||
showChangePasswordDialog();
|
||||
setState({
|
||||
...state,
|
||||
showChangePasswordDialog: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleCopyOpenIdBtnClick = async () => {
|
||||
@ -49,35 +59,47 @@ const UserDetail: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col justify-start items-start">
|
||||
<Header />
|
||||
<div className="mx-auto max-w-4xl w-full px-3 py-6 flex flex-col justify-start items-start space-y-4">
|
||||
<p className="text-3xl mt-2 mb-4">{user?.name}</p>
|
||||
<p className="leading-8 flex flex-row justify-start items-center">
|
||||
<span className="mr-3 text-gray-500 font-mono">Email: </span>
|
||||
{user?.email}
|
||||
</p>
|
||||
<p className="leading-8 flex flex-row justify-start items-center">
|
||||
<span className="mr-3 text-gray-500 font-mono">Password: </span>
|
||||
<button className="border rounded-md px-2 leading-8 hover:shadow" onClick={handleChangePasswordBtnClick}>
|
||||
Change
|
||||
</button>
|
||||
</p>
|
||||
<p className="leading-8 flex flex-row justify-start items-center">
|
||||
<span className="mr-3 text-gray-500 font-mono">OpenID:</span>
|
||||
<input type="text" value={user?.openId} readOnly className="border shrink rounded-md px-3 pr-5 shadow-inner truncate" />
|
||||
<button className="-ml-6 bg-white text-gray-600 hover:text-black" onClick={handleCopyOpenIdBtnClick}>
|
||||
<Icon.Clipboard className="w-4 h-auto" />
|
||||
</button>
|
||||
<button
|
||||
className="border ml-4 rounded-md px-2 leading-8 border-red-600 text-red-600 bg-red-50 hover:shadow"
|
||||
onClick={handleResetOpenIdBtnClick}
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
</p>
|
||||
<>
|
||||
<div className="w-full h-full flex flex-col justify-start items-start">
|
||||
<Header />
|
||||
<div className="mx-auto max-w-4xl w-full px-3 py-6 flex flex-col justify-start items-start space-y-4">
|
||||
<p className="text-3xl mt-2 mb-4">{user?.name}</p>
|
||||
<p className="leading-8 flex flex-row justify-start items-center">
|
||||
<span className="mr-3 text-gray-500 font-mono">Email: </span>
|
||||
{user?.email}
|
||||
</p>
|
||||
<p className="leading-8 flex flex-row justify-start items-center">
|
||||
<span className="mr-3 text-gray-500 font-mono">Password: </span>
|
||||
<button className="border rounded-md px-2 leading-8 hover:shadow" onClick={handleChangePasswordBtnClick}>
|
||||
Change
|
||||
</button>
|
||||
</p>
|
||||
<p className="leading-8 flex flex-row justify-start items-center">
|
||||
<span className="mr-3 text-gray-500 font-mono">OpenID:</span>
|
||||
<input type="text" value={user?.openId} readOnly className="border shrink rounded-md px-3 pr-5 shadow-inner truncate" />
|
||||
<button className="-ml-6 bg-white text-gray-600 hover:text-black" onClick={handleCopyOpenIdBtnClick}>
|
||||
<Icon.Clipboard className="w-4 h-auto" />
|
||||
</button>
|
||||
<button
|
||||
className="border ml-4 rounded-md px-2 leading-8 border-red-600 text-red-600 bg-red-50 hover:shadow"
|
||||
onClick={handleResetOpenIdBtnClick}
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{state.showChangePasswordDialog && (
|
||||
<ChangePasswordDialog
|
||||
onClose={() => {
|
||||
setState({
|
||||
...state,
|
||||
showChangePasswordDialog: false,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -7,17 +7,19 @@ import useLoading from "../hooks/useLoading";
|
||||
import Icon from "../components/Icon";
|
||||
import toastHelper from "../components/Toast";
|
||||
import Dropdown from "../components/common/Dropdown";
|
||||
import showCreateShortcutDialog from "../components/CreateShortcutDialog";
|
||||
import showUpsertWorkspaceUserDialog from "../components/UpsertWorkspaceUserDialog";
|
||||
import Header from "../components/Header";
|
||||
import ShortcutListView from "../components/ShortcutListView";
|
||||
import MemberListView from "../components/MemberListView";
|
||||
import WorkspaceSetting from "../components/WorkspaceSetting";
|
||||
import CreateShortcutDialog from "../components/CreateShortcutDialog";
|
||||
import UpsertWorkspaceUserDialog from "../components/UpsertWorkspaceUserDialog";
|
||||
|
||||
interface State {
|
||||
workspace: Workspace;
|
||||
workspaceUser: WorkspaceUser;
|
||||
userList: WorkspaceUser[];
|
||||
showCreateShortcutDialog: boolean;
|
||||
showUpsertWorkspaceUserDialog: boolean;
|
||||
}
|
||||
|
||||
const WorkspaceDetail: React.FC = () => {
|
||||
@ -30,6 +32,8 @@ const WorkspaceDetail: React.FC = () => {
|
||||
workspace: unknownWorkspace,
|
||||
workspaceUser: unknownWorkspaceUser,
|
||||
userList: [],
|
||||
showCreateShortcutDialog: false,
|
||||
showUpsertWorkspaceUserDialog: false,
|
||||
});
|
||||
const loadingState = useLoading();
|
||||
|
||||
@ -53,6 +57,7 @@ const WorkspaceDetail: React.FC = () => {
|
||||
])
|
||||
.then(([, workspaceUser, workspaceUserList]) => {
|
||||
setState({
|
||||
...state,
|
||||
workspace,
|
||||
workspaceUser,
|
||||
userList: workspaceUserList,
|
||||
@ -70,86 +75,130 @@ const WorkspaceDetail: React.FC = () => {
|
||||
}, [location.hash]);
|
||||
|
||||
const handleCreateShortcutButtonClick = () => {
|
||||
showCreateShortcutDialog(state.workspace.id, undefined, async () => {
|
||||
if (location.hash !== "#shortcuts") {
|
||||
navigate("#shortcuts");
|
||||
}
|
||||
setState({
|
||||
...state,
|
||||
showCreateShortcutDialog: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpsertWorkspaceMemberButtonClick = () => {
|
||||
showUpsertWorkspaceUserDialog(state.workspace.id, async () => {
|
||||
const workspaceUserList = await workspaceService.getWorkspaceUserList(state.workspace.id);
|
||||
setState({
|
||||
...state,
|
||||
userList: workspaceUserList,
|
||||
});
|
||||
|
||||
if (location.hash !== "#members") {
|
||||
navigate("#members");
|
||||
}
|
||||
setState({
|
||||
...state,
|
||||
showUpsertWorkspaceUserDialog: true,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col justify-start items-start">
|
||||
<Header />
|
||||
<div className="mx-auto max-w-4xl w-full px-3 pb-6 flex flex-col justify-start items-start">
|
||||
<div className="w-full flex flex-row justify-between items-center mt-4 mb-4">
|
||||
<div className="flex flex-row justify-start items-center space-x-4">
|
||||
<NavLink to="#shortcuts" className={`${location.hash === "#shortcuts" && "underline"}`}>
|
||||
Shortcuts
|
||||
</NavLink>
|
||||
<NavLink to="#members" className={`${location.hash === "#members" && "underline"}`}>
|
||||
Members
|
||||
</NavLink>
|
||||
<NavLink to="#setting" className={`${location.hash === "#setting" && "underline"}`}>
|
||||
Setting
|
||||
</NavLink>
|
||||
</div>
|
||||
<div>
|
||||
<Dropdown
|
||||
trigger={
|
||||
<button className="w-32 flex flex-row justify-start items-center border px-3 leading-10 rounded-lg cursor-pointer hover:shadow">
|
||||
<Icon.Plus className="w-4 h-auto mr-1" /> Add new...
|
||||
</button>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<button
|
||||
className="w-full flex flex-row justify-start items-center px-3 leading-10 rounded cursor-pointer hover:bg-gray-100"
|
||||
onClick={handleCreateShortcutButtonClick}
|
||||
>
|
||||
Shortcut
|
||||
<>
|
||||
<div className="w-full h-full flex flex-col justify-start items-start">
|
||||
<Header />
|
||||
<div className="mx-auto max-w-4xl w-full px-3 pb-6 flex flex-col justify-start items-start">
|
||||
<div className="w-full flex flex-row justify-between items-center mt-4 mb-4">
|
||||
<div className="flex flex-row justify-start items-center space-x-4">
|
||||
<NavLink to="#shortcuts" className={`${location.hash === "#shortcuts" && "underline"}`}>
|
||||
Shortcuts
|
||||
</NavLink>
|
||||
<NavLink to="#members" className={`${location.hash === "#members" && "underline"}`}>
|
||||
Members
|
||||
</NavLink>
|
||||
<NavLink to="#setting" className={`${location.hash === "#setting" && "underline"}`}>
|
||||
Setting
|
||||
</NavLink>
|
||||
</div>
|
||||
<div>
|
||||
<Dropdown
|
||||
trigger={
|
||||
<button className="w-32 flex flex-row justify-start items-center border px-3 leading-10 rounded-lg cursor-pointer hover:shadow">
|
||||
<Icon.Plus className="w-4 h-auto mr-1" /> Add new...
|
||||
</button>
|
||||
<button
|
||||
className="w-full flex flex-row justify-start items-center px-3 leading-10 rounded cursor-pointer hover:bg-gray-100"
|
||||
onClick={handleUpsertWorkspaceMemberButtonClick}
|
||||
>
|
||||
Member
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
actionsClassName="!w-32"
|
||||
/>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<button
|
||||
className="w-full flex flex-row justify-start items-center px-3 leading-10 rounded cursor-pointer hover:bg-gray-100"
|
||||
onClick={handleCreateShortcutButtonClick}
|
||||
>
|
||||
Shortcut
|
||||
</button>
|
||||
<button
|
||||
className="w-full flex flex-row justify-start items-center px-3 leading-10 rounded cursor-pointer hover:bg-gray-100"
|
||||
onClick={handleUpsertWorkspaceMemberButtonClick}
|
||||
>
|
||||
Member
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
actionsClassName="!w-32"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{loadingState.isLoading ? (
|
||||
<div className="py-4 w-full flex flex-row justify-center items-center">
|
||||
<Icon.Loader className="mr-2 w-5 h-auto animate-spin" />
|
||||
loading
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{location.hash === "#shortcuts" && <ShortcutListView workspaceId={state.workspace.id} shortcutList={shortcutList} />}
|
||||
{location.hash === "#members" && (
|
||||
<MemberListView
|
||||
key={Date.now()}
|
||||
workspaceId={state.workspace.id}
|
||||
workspaceUser={state.workspaceUser}
|
||||
userList={state.userList}
|
||||
/>
|
||||
)}
|
||||
{location.hash === "#setting" && <WorkspaceSetting workspaceId={state.workspace.id} />}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{loadingState.isLoading ? (
|
||||
<div className="py-4 w-full flex flex-row justify-center items-center">
|
||||
<Icon.Loader className="mr-2 w-5 h-auto animate-spin" />
|
||||
loading
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{location.hash === "#shortcuts" && <ShortcutListView workspaceId={state.workspace.id} shortcutList={shortcutList} />}
|
||||
{location.hash === "#members" && (
|
||||
<MemberListView workspaceId={state.workspace.id} workspaceUser={state.workspaceUser} userList={state.userList} />
|
||||
)}
|
||||
{location.hash === "#setting" && <WorkspaceSetting workspaceId={state.workspace.id} />}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{state.showCreateShortcutDialog && (
|
||||
<CreateShortcutDialog
|
||||
workspaceId={state.workspace.id}
|
||||
onClose={() => {
|
||||
setState({
|
||||
...state,
|
||||
showCreateShortcutDialog: false,
|
||||
});
|
||||
}}
|
||||
onConfirm={() => {
|
||||
setState({
|
||||
...state,
|
||||
showCreateShortcutDialog: false,
|
||||
});
|
||||
if (location.hash !== "#shortcuts") {
|
||||
navigate("#shortcuts");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{state.showUpsertWorkspaceUserDialog && (
|
||||
<UpsertWorkspaceUserDialog
|
||||
workspaceId={state.workspace.id}
|
||||
onClose={() => {
|
||||
setState({
|
||||
...state,
|
||||
showUpsertWorkspaceUserDialog: false,
|
||||
});
|
||||
}}
|
||||
onConfirm={async () => {
|
||||
const workspaceUserList = await workspaceService.getWorkspaceUserList(state.workspace.id);
|
||||
setState({
|
||||
...state,
|
||||
userList: workspaceUserList,
|
||||
showUpsertWorkspaceUserDialog: false,
|
||||
});
|
||||
|
||||
if (location.hash !== "#members") {
|
||||
navigate("#members");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
7
web/src/types/view.d.ts
vendored
7
web/src/types/view.d.ts
vendored
@ -1,7 +0,0 @@
|
||||
interface DialogProps {
|
||||
destroy: FunctionType;
|
||||
}
|
||||
|
||||
interface DialogCallback {
|
||||
destroy: FunctionType;
|
||||
}
|
Reference in New Issue
Block a user