diff options
author | crupest <crupest@outlook.com> | 2020-11-20 20:14:12 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-11-20 20:14:12 +0800 |
commit | 517101c4475498976fbf4a3cacf8b408cee545e8 (patch) | |
tree | 56b8d18178beed0c4a131e8f249f1edf69aefbfd | |
parent | d7afe21e3b5eca39ea6d2c67e826911a1bf102de (diff) | |
download | timeline-517101c4475498976fbf4a3cacf8b408cee545e8.tar.gz timeline-517101c4475498976fbf4a3cacf8b408cee545e8.tar.bz2 timeline-517101c4475498976fbf4a3cacf8b408cee545e8.zip |
...
-rw-r--r-- | FrontEnd/src/app/http/user.ts | 10 | ||||
-rw-r--r-- | FrontEnd/src/app/views/admin/UserAdmin.tsx | 213 |
2 files changed, 165 insertions, 58 deletions
diff --git a/FrontEnd/src/app/http/user.ts b/FrontEnd/src/app/http/user.ts index 243846d1..929956d0 100644 --- a/FrontEnd/src/app/http/user.ts +++ b/FrontEnd/src/app/http/user.ts @@ -12,10 +12,14 @@ import { convertToNotModified, } from "./common"; +export const kUserManagement = "UserManagement"; +export const kAllTimelineManagement = "AllTimelineManagement"; +export const kHighlightTimelineManagement = "HighlightTimelineManagement"; + export const kUserPermissionList = [ - "UserManagement", - "AllTimelineManagement", - "HighlightTimelineManagement", + kUserManagement, + kAllTimelineManagement, + kHighlightTimelineManagement, ] as const; export type UserPermission = typeof kUserPermissionList[number]; diff --git a/FrontEnd/src/app/views/admin/UserAdmin.tsx b/FrontEnd/src/app/views/admin/UserAdmin.tsx index 65cd3b74..bd60381f 100644 --- a/FrontEnd/src/app/views/admin/UserAdmin.tsx +++ b/FrontEnd/src/app/views/admin/UserAdmin.tsx @@ -1,64 +1,20 @@ import React, { useState, useEffect } from "react"; +import clsx from "clsx"; import { ListGroup, Row, Col, Spinner, Button } from "react-bootstrap"; import InlineSVG from "react-inlinesvg"; import PencilSquareIcon from "bootstrap-icons/icons/pencil-square.svg"; -import OperationDialog from "../common/OperationDialog"; +import OperationDialog, { + OperationBoolInputInfo, +} from "../common/OperationDialog"; import { User, AuthUser } from "@/services/user"; -import { getHttpUserClient, HttpUser } from "@/http/user"; -import clsx from "clsx"; - -const kModify = "modify"; -const kDelete = "delete"; - -type TModify = typeof kModify; -type TDelete = typeof kDelete; - -type ContextMenuItem = TModify | TDelete; - -interface UserCardProps { - on: { [key in ContextMenuItem]: () => void }; - user: User; -} - -const UserItem: React.FC<UserCardProps> = ({ user, on }) => { - const [editMaskVisible, setEditMaskVisible] = React.useState<boolean>(false); - - return ( - <ListGroup.Item className="admin-user-item"> - <InlineSVG - src={PencilSquareIcon} - className="float-right icon-button text-warning" - onClick={() => setEditMaskVisible(true)} - /> - <h4 className="text-primary">{user.username}</h4> - <div className="text-secondary">nickname: {user.nickname}</div> - <div className="text-secondary">unique id: {user.uniqueId}</div> - <div className="text-secondary"> - permissions:{" "} - {user.permissions.map((permission) => { - return ( - <span key={permission} className="text-danger"> - {permission}{" "} - </span> - ); - })} - </div> - <div - className={clsx("edit-mask", !editMaskVisible && "d-none")} - onClick={() => setEditMaskVisible(false)} - > - <button className="text-button primary" onClick={on["modify"]}> - Modify - </button> - <button className="text-button danger" onClick={on["delete"]}> - Delete - </button> - </div> - </ListGroup.Item> - ); -}; +import { + getHttpUserClient, + HttpUser, + kUserPermissionList, + UserPermission, +} from "@/http/user"; interface DialogProps<TData = undefined, TReturn = undefined> { open: boolean; @@ -167,6 +123,127 @@ const UserModifyDialog: React.FC<DialogProps< ); }; +const UserPermissionModifyDialog: React.FC<DialogProps< + { + username: string; + permissions: UserPermission[]; + }, + UserPermission[] +>> = ({ open, close, token, data: { username, permissions }, onSuccess }) => { + const oldPermissionBoolList: boolean[] = kUserPermissionList.map( + (permission) => permissions.includes(permission) + ); + + return ( + <OperationDialog + open={open} + close={close} + title="Caution" + titleColor="dangerous" + inputPrompt={() => ( + <> + You are modify permission of user + <UsernameLabel>{username}</UsernameLabel> ! + </> + )} + inputScheme={kUserPermissionList.map<OperationBoolInputInfo>( + (permission, index) => ({ + type: "bool", + label: permission, + initValue: oldPermissionBoolList[index], + }) + )} + onProcess={async (newPermissionBoolList): Promise<boolean[]> => { + for (let index = 0; index < kUserPermissionList.length; index++) { + const oldValue = oldPermissionBoolList[index]; + const newValue = newPermissionBoolList[index]; + const permission = kUserPermissionList[index]; + if (oldValue === newValue) continue; + if (newValue) { + await getHttpUserClient().putUserPermission( + username, + permission, + token + ); + } else { + await getHttpUserClient().deleteUserPermission( + username, + permission, + token + ); + } + } + return newPermissionBoolList; + }} + onSuccessAndClose={(newPermissionBoolList: boolean[]) => { + const permissions: UserPermission[] = []; + for (let index = 0; index < kUserPermissionList.length; index++) { + if (newPermissionBoolList[index]) { + permissions.push(kUserPermissionList[index]); + } + } + onSuccess(permissions); + }} + /> + ); +}; + +const kModify = "modify"; +const kModifyPermission = "permission"; +const kDelete = "delete"; + +type TModify = typeof kModify; +type TModifyPermission = typeof kModifyPermission; +type TDelete = typeof kDelete; + +type ContextMenuItem = TModify | TModifyPermission | TDelete; + +interface UserItemProps { + on: { [key in ContextMenuItem]: () => void }; + user: User; +} + +const UserItem: React.FC<UserItemProps> = ({ user, on }) => { + const [editMaskVisible, setEditMaskVisible] = React.useState<boolean>(false); + + return ( + <ListGroup.Item className="admin-user-item"> + <InlineSVG + src={PencilSquareIcon} + className="float-right icon-button text-warning" + onClick={() => setEditMaskVisible(true)} + /> + <h4 className="text-primary">{user.username}</h4> + <div className="text-secondary">nickname: {user.nickname}</div> + <div className="text-secondary">unique id: {user.uniqueId}</div> + <div className="text-secondary"> + permissions:{" "} + {user.permissions.map((permission) => { + return ( + <span key={permission} className="text-danger"> + {permission}{" "} + </span> + ); + })} + </div> + <div + className={clsx("edit-mask", !editMaskVisible && "d-none")} + onClick={() => setEditMaskVisible(false)} + > + <button className="text-button primary" onClick={on[kModify]}> + Modify + </button> + <button className="text-button primary" onClick={on[kModifyPermission]}> + Modify Permission + </button> + <button className="text-button danger" onClick={on[kDelete]}> + Delete + </button> + </div> + </ListGroup.Item> + ); +}; + interface UserAdminProps { user: AuthUser; } @@ -181,6 +258,11 @@ const UserAdmin: React.FC<UserAdminProps> = (props) => { type: TModify; user: HttpUser; } + | { + type: TModifyPermission; + username: string; + permissions: UserPermission[]; + } | { type: TDelete; username: string }; const [users, setUsers] = useState<User[] | null>(null); @@ -220,7 +302,7 @@ const UserAdmin: React.FC<UserAdminProps> = (props) => { /> ); break; - case "delete": + case kDelete: dialogNode = ( <UserDeleteDialog open @@ -242,6 +324,20 @@ const UserAdmin: React.FC<UserAdminProps> = (props) => { /> ); break; + case kModifyPermission: + dialogNode = ( + <UserPermissionModifyDialog + open + close={() => setDialog(null)} + token={token} + data={{ + username: dialog.username, + permissions: dialog.permissions, + }} + onSuccess={updateUsers} + /> + ); + break; } } @@ -258,6 +354,13 @@ const UserAdmin: React.FC<UserAdminProps> = (props) => { user, }); }, + permission: () => { + setDialog({ + type: kModifyPermission, + username: user.username, + permissions: user.permissions, + }); + }, delete: () => { setDialog({ type: "delete", |