mirror of
https://github.com/aykhans/slash-e.git
synced 2025-04-18 21:19:44 +00:00
feat: update pages
This commit is contained in:
parent
89740fa160
commit
407a8cc7fb
@ -28,7 +28,7 @@ const Header: React.FC = () => {
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
trigger={
|
trigger={
|
||||||
<button className="flex flex-row justify-end items-center cursor-pointer">
|
<button className="flex flex-row justify-end items-center cursor-pointer">
|
||||||
<span>{user?.displayName}</span>
|
<span>{user.nickname}</span>
|
||||||
<Icon.ChevronDown className="ml-1 w-5 h-auto text-gray-600" />
|
<Icon.ChevronDown className="ml-1 w-5 h-auto text-gray-600" />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ const ShortcutListView: React.FC<Props> = (props: Props) => {
|
|||||||
<span className="text-gray-400 text-sm ml-2">({shortcut.description})</span>
|
<span className="text-gray-400 text-sm ml-2">({shortcut.description})</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row justify-end items-center">
|
<div className="flex flex-row justify-end items-center">
|
||||||
<span className="w-16 truncate mr-2 text-gray-600">{shortcut.creator.displayName}</span>
|
<span className="w-16 truncate mr-2 text-gray-600">{shortcut.creator.nickname}</span>
|
||||||
<Tooltip title="Copy link" variant="solid" placement="top">
|
<Tooltip title="Copy link" variant="solid" placement="top">
|
||||||
<button
|
<button
|
||||||
className="cursor-pointer mr-4 hover:opacity-80"
|
className="cursor-pointer mr-4 hover:opacity-80"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Outlet } from "react-router-dom";
|
import { Outlet } from "react-router-dom";
|
||||||
import Header from "../components/Header";
|
import Header from "../components/Header";
|
||||||
|
|
||||||
const UserDetail: React.FC = () => {
|
const Root: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full flex flex-col justify-start items-start">
|
<div className="w-full h-full flex flex-col justify-start items-start">
|
||||||
<Header />
|
<Header />
|
||||||
@ -10,4 +10,4 @@ const UserDetail: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UserDetail;
|
export default Root;
|
||||||
|
@ -1,15 +1,24 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { userService, shortcutService } from "../services";
|
import { userService, shortcutService } from "../services";
|
||||||
import { useAppSelector } from "../store";
|
import { useAppSelector } from "../store";
|
||||||
import useLoading from "../hooks/useLoading";
|
import useLoading from "../hooks/useLoading";
|
||||||
import Icon from "../components/Icon";
|
import Icon from "../components/Icon";
|
||||||
|
import Dropdown from "../components/common/Dropdown";
|
||||||
import ShortcutListView from "../components/ShortcutListView";
|
import ShortcutListView from "../components/ShortcutListView";
|
||||||
|
import CreateShortcutDialog from "../components/CreateShortcutDialog";
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
showCreateShortcutDialog: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const Home: React.FC = () => {
|
const Home: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
const { shortcutList } = useAppSelector((state) => state.shortcut);
|
const { shortcutList } = useAppSelector((state) => state.shortcut);
|
||||||
|
const [state, setState] = useState<State>({
|
||||||
|
showCreateShortcutDialog: false,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!userService.getState().user) {
|
if (!userService.getState().user) {
|
||||||
@ -22,11 +31,38 @@ const Home: React.FC = () => {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const setShowCreateShortcutDialog = (show: boolean) => {
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
showCreateShortcutDialog: show,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mx-auto max-w-4xl w-full px-3 py-6 flex flex-col justify-start items-start">
|
<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">
|
<div className="w-full flex flex-row justify-between items-center mb-4">
|
||||||
<span className="font-mono text-gray-400">Workspace List</span>
|
<span className="font-mono text-gray-400">Shortcuts</span>
|
||||||
|
<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={() => setShowCreateShortcutDialog(true)}
|
||||||
|
>
|
||||||
|
Shortcut
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
actionsClassName="!w-32"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{loadingState.isLoading ? (
|
{loadingState.isLoading ? (
|
||||||
<div className="py-4 w-full flex flex-row justify-center items-center">
|
<div className="py-4 w-full flex flex-row justify-center items-center">
|
||||||
@ -37,6 +73,10 @@ const Home: React.FC = () => {
|
|||||||
<ShortcutListView shortcutList={shortcutList} />
|
<ShortcutListView shortcutList={shortcutList} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{state.showCreateShortcutDialog && (
|
||||||
|
<CreateShortcutDialog onClose={() => setShowCreateShortcutDialog(false)} onConfirm={() => setShowCreateShortcutDialog(false)} />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
import { useEffect, useState } from "react";
|
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import { getShortcutWithNameAndWorkspaceName } from "../helpers/api";
|
|
||||||
import useLoading from "../hooks/useLoading";
|
|
||||||
|
|
||||||
interface State {
|
|
||||||
errMessage?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ShortcutRedirector: React.FC = () => {
|
|
||||||
const params = useParams();
|
|
||||||
const [state, setState] = useState<State>();
|
|
||||||
const loadingState = useLoading();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const workspaceName = params.workspaceName || "";
|
|
||||||
const shortcutName = params.shortcutName || "";
|
|
||||||
getShortcutWithNameAndWorkspaceName(workspaceName, shortcutName)
|
|
||||||
.then(({ data: shortcut }) => {
|
|
||||||
if (shortcut) {
|
|
||||||
window.location.href = shortcut.link;
|
|
||||||
} else {
|
|
||||||
setState({
|
|
||||||
errMessage: "Not found",
|
|
||||||
});
|
|
||||||
loadingState.setFinish();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
setState({
|
|
||||||
errMessage: error.response.data.error || "Error occurred",
|
|
||||||
});
|
|
||||||
loadingState.setFinish();
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return loadingState.isLoading ? null : (
|
|
||||||
<div className="w-full pt-24 text-center font-mono text-xl">
|
|
||||||
<p>{state?.errMessage}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ShortcutRedirector;
|
|
@ -1,12 +1,8 @@
|
|||||||
import { Button, Input, Tooltip } from "@mui/joy";
|
import { Button } from "@mui/joy";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import toast from "react-hot-toast";
|
|
||||||
import { useAppSelector } from "../store";
|
import { useAppSelector } from "../store";
|
||||||
import { showCommonDialog } from "../components/Alert";
|
|
||||||
import { userService } from "../services";
|
import { userService } from "../services";
|
||||||
import Icon from "../components/Icon";
|
|
||||||
import copy from "copy-to-clipboard";
|
|
||||||
import ChangePasswordDialog from "../components/ChangePasswordDialog";
|
import ChangePasswordDialog from "../components/ChangePasswordDialog";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@ -20,11 +16,13 @@ const UserDetail: React.FC = () => {
|
|||||||
showChangePasswordDialog: false,
|
showChangePasswordDialog: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("here");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!userService.getState().user) {
|
if (!userService.getState().user) {
|
||||||
navigate("/user/auth");
|
navigate("/user/auth");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log("here");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleChangePasswordBtnClick = async () => {
|
const handleChangePasswordBtnClick = async () => {
|
||||||
@ -34,34 +32,10 @@ const UserDetail: React.FC = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCopyOpenIdBtnClick = async () => {
|
|
||||||
if (!user?.openId) {
|
|
||||||
toast.error("OpenID not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(user.openId);
|
|
||||||
toast.success("OpenID copied");
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleResetOpenIdBtnClick = async () => {
|
|
||||||
showCommonDialog({
|
|
||||||
title: "Reset Open API",
|
|
||||||
content: "❗️The existing API will be invalidated and a new one will be generated, are you sure you want to reset?",
|
|
||||||
style: "warning",
|
|
||||||
onConfirm: async () => {
|
|
||||||
await userService.patchUser({
|
|
||||||
id: user?.id as UserId,
|
|
||||||
resetOpenId: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mx-auto max-w-4xl w-full px-3 py-6 flex flex-col justify-start items-start space-y-4">
|
<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?.displayName}</p>
|
<p className="text-3xl mt-2 mb-4">{user?.nickname}</p>
|
||||||
<p className="leading-8 flex flex-row justify-start items-center">
|
<p className="leading-8 flex flex-row justify-start items-center">
|
||||||
<span className="mr-3 text-gray-500 font-mono">Email: </span>
|
<span className="mr-3 text-gray-500 font-mono">Email: </span>
|
||||||
{user?.email}
|
{user?.email}
|
||||||
@ -72,21 +46,6 @@ const UserDetail: React.FC = () => {
|
|||||||
Change
|
Change
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{/* Do not display open api related field right now. */}
|
|
||||||
{false && (
|
|
||||||
<div className="leading-8 flex flex-row justify-start items-center">
|
|
||||||
<span className="mr-3 text-gray-500 font-mono">OpenID:</span>
|
|
||||||
<Input type="text" className="w-48" value={user?.openId} readOnly />
|
|
||||||
<Tooltip title="Copy OpenID" variant="solid" placement="top">
|
|
||||||
<button className="-ml-6 z-1 bg-white text-gray-600 hover:text-black" onClick={handleCopyOpenIdBtnClick}>
|
|
||||||
<Icon.Clipboard className="w-4 h-auto" />
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
<Button className="!ml-6" variant="soft" color="warning" onClick={handleResetOpenIdBtnClick}>
|
|
||||||
Reset
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{state.showChangePasswordDialog && (
|
{state.showChangePasswordDialog && (
|
||||||
|
@ -5,7 +5,6 @@ import Root from "../layout/Root";
|
|||||||
import Auth from "../pages/Auth";
|
import Auth from "../pages/Auth";
|
||||||
import Home from "../pages/Home";
|
import Home from "../pages/Home";
|
||||||
import UserDetail from "../pages/UserDetail";
|
import UserDetail from "../pages/UserDetail";
|
||||||
import ShortcutRedirector from "../pages/ShortcutRedirector";
|
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@ -30,6 +29,7 @@ const router = createBrowserRouter([
|
|||||||
if (isNullorUndefined(user)) {
|
if (isNullorUndefined(user)) {
|
||||||
return redirect("/user/auth");
|
return redirect("/user/auth");
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -46,14 +46,11 @@ const router = createBrowserRouter([
|
|||||||
if (isNullorUndefined(user)) {
|
if (isNullorUndefined(user)) {
|
||||||
return redirect("/user/auth");
|
return redirect("/user/auth");
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/:shortcutName",
|
|
||||||
element: <ShortcutRedirector />,
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
4
web/src/types/modules/user.d.ts
vendored
4
web/src/types/modules/user.d.ts
vendored
@ -9,9 +9,9 @@ interface User {
|
|||||||
updatedTs: TimeStamp;
|
updatedTs: TimeStamp;
|
||||||
rowStatus: RowStatus;
|
rowStatus: RowStatus;
|
||||||
|
|
||||||
|
username: string;
|
||||||
|
nickname: string;
|
||||||
email: string;
|
email: string;
|
||||||
displayName: string;
|
|
||||||
openId: string;
|
|
||||||
role: Role;
|
role: Role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user