From 232a19d7dfe0e3847b3a9a9a9be83485ffb9031c Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 30 May 2020 16:23:25 +0800 Subject: Merge front end to this repo. But I need to wait for aspnet core support for custom port and package manager for dev server. --- Timeline/ClientApp/src/admin/UserAdmin.tsx | 458 +++++++++++++++++++++++++++++ 1 file changed, 458 insertions(+) create mode 100644 Timeline/ClientApp/src/admin/UserAdmin.tsx (limited to 'Timeline/ClientApp/src/admin/UserAdmin.tsx') diff --git a/Timeline/ClientApp/src/admin/UserAdmin.tsx b/Timeline/ClientApp/src/admin/UserAdmin.tsx new file mode 100644 index 00000000..afef79c9 --- /dev/null +++ b/Timeline/ClientApp/src/admin/UserAdmin.tsx @@ -0,0 +1,458 @@ +import React, { useState, useEffect } from 'react'; +import { + ListGroupItem, + Row, + Col, + UncontrolledDropdown, + DropdownToggle, + DropdownMenu, + DropdownItem, + Spinner, + Button, +} from 'reactstrap'; +import axios from 'axios'; + +import OperationDialog from '../common/OperationDialog'; + +import { User, UserWithToken } from '../data/user'; +import { apiBaseUrl } from '../config'; + +async function fetchUserList(_token: string): Promise { + const res = await axios.get(`${apiBaseUrl}/users`); + return res.data; +} + +interface CreateUserInfo { + username: string; + password: string; + administrator: boolean; +} + +async function createUser(user: CreateUserInfo, token: string): Promise { + const res = await axios.post( + `${apiBaseUrl}/userop/createuser?token=${token}`, + user + ); + return res.data; +} + +function deleteUser(username: string, token: string): Promise { + return axios.delete(`${apiBaseUrl}/users/${username}?token=${token}`); +} + +function changeUsername( + oldUsername: string, + newUsername: string, + token: string +): Promise { + return axios.patch(`${apiBaseUrl}/users/${oldUsername}?token=${token}`, { + username: newUsername, + }); +} + +function changePassword( + username: string, + newPassword: string, + token: string +): Promise { + return axios.patch(`${apiBaseUrl}/users/${username}?token=${token}`, { + password: newPassword, + }); +} + +function changePermission( + username: string, + newPermission: boolean, + token: string +): Promise { + return axios.patch(`${apiBaseUrl}/users/${username}?token=${token}`, { + administrator: newPermission, + }); +} + +const kChangeUsername = 'changeusername'; +const kChangePassword = 'changepassword'; +const kChangePermission = 'changepermission'; +const kDelete = 'delete'; + +type TChangeUsername = typeof kChangeUsername; +type TChangePassword = typeof kChangePassword; +type TChangePermission = typeof kChangePermission; +type TDelete = typeof kDelete; + +type ContextMenuItem = + | TChangeUsername + | TChangePassword + | TChangePermission + | TDelete; + +interface UserCardProps { + onContextMenu: (item: ContextMenuItem) => void; + user: User; +} + +const UserItem: React.FC = (props) => { + const user = props.user; + + const createClickCallback = (item: ContextMenuItem): (() => void) => { + return () => { + props.onContextMenu(item); + }; + }; + + return ( + + + +

{user.username}

+ + {user.administrator ? 'administrator' : 'user'} + + + + + + Manage + + + + Change Username + + + Change Password + + + Change Permission + + + Delete + + + + +
+
+ ); +}; + +interface DialogProps { + open: boolean; + close: () => void; +} + +interface CreateUserDialogProps extends DialogProps { + process: (user: CreateUserInfo) => Promise; +} + +const CreateUserDialog: React.FC = (props) => { + return ( + + props.process({ + username: username as string, + password: password as string, + administrator: administrator as boolean, + }) + } + close={props.close} + open={props.open} + /> + ); +}; + +const UsernameLabel: React.FC = (props) => { + return {props.children}; +}; + +interface UserDeleteDialogProps extends DialogProps { + username: string; + process: () => Promise; +} + +const UserDeleteDialog: React.FC = (props) => { + return ( + ( + <> + {'You are deleting user '} + {props.username} + {' !'} + + )} + onProcess={props.process} + /> + ); +}; + +interface UserModifyDialogProps extends DialogProps { + username: string; + process: (value: T) => Promise; +} + +const UserChangeUsernameDialog: React.FC> = ( + props +) => { + return ( + ( + <> + {'You are change the username of user '} + {props.username} + {' !'} + + )} + inputScheme={[{ type: 'text', label: 'New Username' }]} + onProcess={([newUsername]) => { + return props.process(newUsername as string); + }} + /> + ); +}; + +const UserChangePasswordDialog: React.FC> = ( + props +) => { + return ( + ( + <> + {'You are change the password of user '} + {props.username} + {' !'} + + )} + inputScheme={[{ type: 'text', label: 'New Password' }]} + onProcess={([newPassword]) => { + return props.process(newPassword as string); + }} + /> + ); +}; + +interface UserChangePermissionDialogProps extends DialogProps { + username: string; + newPermission: boolean; + process: () => Promise; +} + +const UserChangePermissionDialog: React.FC = ( + props +) => { + return ( + ( + <> + {'You are change user '} + {props.username} + {' to '} + + {props.newPermission ? 'administrator' : 'normal user'} + + {' !'} + + )} + onProcess={props.process} + /> + ); +}; + +interface UserAdminProps { + user: UserWithToken; +} + +const UserAdmin: React.FC = (props) => { + type DialogInfo = + | null + | { + type: 'create'; + } + | { type: 'delete'; username: string } + | { + type: TChangeUsername; + username: string; + } + | { + type: TChangePassword; + username: string; + } + | { + type: TChangePermission; + username: string; + newPermission: boolean; + }; + + const [users, setUsers] = useState(null); + const [dialog, setDialog] = useState(null); + + const token = props.user.token; + + useEffect(() => { + let subscribe = true; + fetchUserList(props.user.token).then((us) => { + if (subscribe) { + setUsers(us); + } + }); + return () => { + subscribe = false; + }; + }, [props.user]); + + let dialogNode: React.ReactNode; + if (dialog) + switch (dialog.type) { + case 'create': + dialogNode = ( + setDialog(null)} + process={async (user) => { + const u = await createUser(user, token); + setUsers((oldUsers) => [...oldUsers!, u]); + }} + /> + ); + break; + case 'delete': + dialogNode = ( + setDialog(null)} + username={dialog.username} + process={async () => { + await deleteUser(dialog.username, token); + setUsers((oldUsers) => + oldUsers!.filter((u) => u.username !== dialog.username) + ); + }} + /> + ); + break; + case kChangeUsername: + dialogNode = ( + setDialog(null)} + username={dialog.username} + process={async (newUsername) => { + await changeUsername(dialog.username, newUsername, token); + setUsers((oldUsers) => { + const users = oldUsers!.slice(); + users.find( + (u) => u.username === dialog.username + )!.username = newUsername; + return users; + }); + }} + /> + ); + break; + case kChangePassword: + dialogNode = ( + setDialog(null)} + username={dialog.username} + process={async (newPassword) => { + await changePassword(dialog.username, newPassword, token); + }} + /> + ); + break; + case kChangePermission: { + const newPermission = dialog.newPermission; + dialogNode = ( + setDialog(null)} + username={dialog.username} + newPermission={newPermission} + process={async () => { + await changePermission(dialog.username, newPermission, token); + setUsers((oldUsers) => { + const users = oldUsers!.slice(); + users.find( + (u) => u.username === dialog.username + )!.administrator = newPermission; + return users; + }); + }} + /> + ); + break; + } + } + + if (users) { + const userComponents = users.map((user) => { + return ( + { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const dialogInfo: any = { + type: item, + username: user.username, + }; + if (item === 'changepermission') { + dialogInfo.newPermission = !user.administrator; + } + setDialog(dialogInfo); + }} + /> + ); + }); + + return ( + <> + + {userComponents} + {dialogNode} + + ); + } else { + return ; + } +}; + +export default UserAdmin; -- cgit v1.2.3