feat(admin): enable multi-users (#67)
* feat(userstore): support ListUsers * feat(userstore): support del users * feat(multiusers): support list users and delete user apis * feat(client/web): add new apis to web client * fix(ui/panes): move each pane out of the container * feat(ui): add admin pane * feat(users): support force set password api * feat(ui/admin-pane): add functions to admin pane * feat(users): support self API and move uploading folder to home * fix(users): remove home folder when deleting user * fix(ui): remove useless function * feat(ui/panes): hide admin menu if user is not admin * fix(server/files): list home path is incorrect * fix(server): 1.listHome return incorrect cwd 2.addUser init folder with incorrect uid 3.check ns before using * test(server): add regression test cases * test(users, files): add e2e test for concurrent operations * fix(test): clean ups
This commit is contained in:
parent
916ec7c2dc
commit
aefaca98b3
28 changed files with 1562 additions and 478 deletions
|
@ -130,6 +130,22 @@ func (cl *FilesClient) List(dirPath string) (*http.Response, *fileshdr.ListResp,
|
|||
return resp, lResp, nil
|
||||
}
|
||||
|
||||
func (cl *FilesClient) ListHome() (*http.Response, *fileshdr.ListResp, []error) {
|
||||
resp, body, errs := cl.r.Get(cl.url("/v1/fs/dirs/home")).
|
||||
AddCookie(cl.token).
|
||||
End()
|
||||
if len(errs) > 0 {
|
||||
return nil, nil, errs
|
||||
}
|
||||
|
||||
lResp := &fileshdr.ListResp{}
|
||||
err := json.Unmarshal([]byte(body), lResp)
|
||||
if err != nil {
|
||||
return nil, nil, append(errs, err)
|
||||
}
|
||||
return resp, lResp, nil
|
||||
}
|
||||
|
||||
func (cl *FilesClient) ListUploadings() (*http.Response, *fileshdr.ListUploadingsResp, []error) {
|
||||
resp, body, errs := cl.r.Get(cl.url("/v1/fs/uploadings")).
|
||||
AddCookie(cl.token).
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/ihexxa/quickshare/src/handlers"
|
||||
"github.com/ihexxa/quickshare/src/handlers/multiusers"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
)
|
||||
|
@ -74,6 +75,30 @@ func (cl *SingleUserClient) AddUser(name, pwd, role string, token *http.Cookie)
|
|||
return resp, auResp, errs
|
||||
}
|
||||
|
||||
func (cl *SingleUserClient) DelUser(id string, token *http.Cookie) (*http.Response, string, []error) {
|
||||
return cl.r.Delete(cl.url("/v1/users/")).
|
||||
AddCookie(token).
|
||||
Param(handlers.UserIDParam, id).
|
||||
End()
|
||||
}
|
||||
|
||||
func (cl *SingleUserClient) ListUsers(token *http.Cookie) (*http.Response, *multiusers.ListUsersResp, []error) {
|
||||
resp, body, errs := cl.r.Get(cl.url("/v1/users/list")).
|
||||
AddCookie(token).
|
||||
End()
|
||||
if len(errs) > 0 {
|
||||
return nil, nil, errs
|
||||
}
|
||||
|
||||
lsResp := &multiusers.ListUsersResp{}
|
||||
err := json.Unmarshal([]byte(body), lsResp)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
return nil, nil, errs
|
||||
}
|
||||
return resp, lsResp, errs
|
||||
}
|
||||
|
||||
func (cl *SingleUserClient) AddRole(role string, token *http.Cookie) (*http.Response, string, []error) {
|
||||
return cl.r.Post(cl.url("/v1/roles/")).
|
||||
AddCookie(token).
|
||||
|
@ -93,7 +118,7 @@ func (cl *SingleUserClient) DelRole(role string, token *http.Cookie) (*http.Resp
|
|||
}
|
||||
|
||||
func (cl *SingleUserClient) ListRoles(token *http.Cookie) (*http.Response, *multiusers.ListRolesResp, []error) {
|
||||
resp, body, errs := cl.r.Get(cl.url("/v1/roles/")).
|
||||
resp, body, errs := cl.r.Get(cl.url("/v1/roles/list")).
|
||||
AddCookie(token).
|
||||
End()
|
||||
if len(errs) > 0 {
|
||||
|
@ -108,3 +133,20 @@ func (cl *SingleUserClient) ListRoles(token *http.Cookie) (*http.Response, *mult
|
|||
}
|
||||
return resp, lsResp, errs
|
||||
}
|
||||
|
||||
func (cl *SingleUserClient) Self(token *http.Cookie) (*http.Response, *multiusers.SelfResp, []error) {
|
||||
resp, body, errs := cl.r.Get(cl.url("/v1/users/self")).
|
||||
AddCookie(token).
|
||||
End()
|
||||
if len(errs) > 0 {
|
||||
return nil, nil, errs
|
||||
}
|
||||
|
||||
selfResp := &multiusers.SelfResp{}
|
||||
err := json.Unmarshal([]byte(body), selfResp)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
return nil, nil, errs
|
||||
}
|
||||
return resp, selfResp, errs
|
||||
}
|
|
@ -1,12 +1,21 @@
|
|||
import axios, { AxiosRequestConfig } from "axios";
|
||||
|
||||
export const defaultTimeout = 10000;
|
||||
export const userIDParam = "uid";
|
||||
|
||||
export interface User {
|
||||
ID: string;
|
||||
Name: string;
|
||||
Pwd: string;
|
||||
Role: string;
|
||||
id: string;
|
||||
name: string;
|
||||
pwd: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface ListUsersResp {
|
||||
users: Array<User>;
|
||||
}
|
||||
|
||||
export interface ListRolesResp {
|
||||
roles: Array<string>;
|
||||
}
|
||||
|
||||
export interface MetadataResp {
|
||||
|
@ -42,7 +51,15 @@ export interface IUsersClient {
|
|||
login: (user: string, pwd: string) => Promise<Response>;
|
||||
logout: () => Promise<Response>;
|
||||
isAuthed: () => Promise<Response>;
|
||||
self: () => Promise<Response>;
|
||||
setPwd: (oldPwd: string, newPwd: string) => Promise<Response>;
|
||||
forceSetPwd: (userID: string, newPwd: string) => Promise<Response>;
|
||||
addUser: (name: string, pwd: string, role: string) => Promise<Response>;
|
||||
delUser: (userID: string) => Promise<Response>;
|
||||
listUsers: () => Promise<Response>;
|
||||
addRole: (role: string) => Promise<Response>;
|
||||
delRole: (role: string) => Promise<Response>;
|
||||
listRoles: () => Promise<Response>;
|
||||
}
|
||||
|
||||
export interface IFilesClient {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { BaseClient, Response } from "./";
|
||||
import { BaseClient, Response, userIDParam } from "./";
|
||||
|
||||
export class UsersClient extends BaseClient {
|
||||
constructor(url: string) {
|
||||
|
@ -16,7 +16,6 @@ export class UsersClient extends BaseClient {
|
|||
});
|
||||
};
|
||||
|
||||
// token cookie is set by browser
|
||||
logout = (): Promise<Response> => {
|
||||
return this.do({
|
||||
method: "post",
|
||||
|
@ -31,7 +30,6 @@ export class UsersClient extends BaseClient {
|
|||
});
|
||||
};
|
||||
|
||||
// token cookie is set by browser
|
||||
setPwd = (oldPwd: string, newPwd: string): Promise<Response> => {
|
||||
return this.do({
|
||||
method: "patch",
|
||||
|
@ -43,8 +41,19 @@ export class UsersClient extends BaseClient {
|
|||
});
|
||||
};
|
||||
|
||||
forceSetPwd = (userID: string, newPwd: string): Promise<Response> => {
|
||||
return this.do({
|
||||
method: "patch",
|
||||
url: `${this.url}/v1/users/pwd/force-set`,
|
||||
data: {
|
||||
id: userID,
|
||||
newPwd,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// token cookie is set by browser
|
||||
adduser = (name: string, pwd: string, role: string): Promise<Response> => {
|
||||
addUser = (name: string, pwd: string, role: string): Promise<Response> => {
|
||||
return this.do({
|
||||
method: "post",
|
||||
url: `${this.url}/v1/users/`,
|
||||
|
@ -55,4 +64,54 @@ export class UsersClient extends BaseClient {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
delUser = (userID: string): Promise<Response> => {
|
||||
return this.do({
|
||||
method: "delete",
|
||||
url: `${this.url}/v1/users/`,
|
||||
params: {
|
||||
[userIDParam]: userID,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
listUsers = (): Promise<Response> => {
|
||||
return this.do({
|
||||
method: "get",
|
||||
url: `${this.url}/v1/users/list`,
|
||||
params: {},
|
||||
});
|
||||
};
|
||||
|
||||
addRole = (role: string): Promise<Response> => {
|
||||
return this.do({
|
||||
method: "post",
|
||||
url: `${this.url}/v1/roles/`,
|
||||
data: { role },
|
||||
});
|
||||
};
|
||||
|
||||
delRole = (role: string): Promise<Response> => {
|
||||
return this.do({
|
||||
method: "delete",
|
||||
url: `${this.url}/v1/roles/`,
|
||||
data: { role },
|
||||
});
|
||||
};
|
||||
|
||||
listRoles = (): Promise<Response> => {
|
||||
return this.do({
|
||||
method: "get",
|
||||
url: `${this.url}/v1/roles/list`,
|
||||
params: {},
|
||||
});
|
||||
};
|
||||
|
||||
self = (): Promise<Response> => {
|
||||
return this.do({
|
||||
method: "get",
|
||||
url: `${this.url}/v1/users/self`,
|
||||
params: {},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,7 +7,14 @@ export class MockUsersClient {
|
|||
private logoutMockResp: Promise<Response>;
|
||||
private isAuthedMockResp: Promise<Response>;
|
||||
private setPwdMockResp: Promise<Response>;
|
||||
private forceSetPwdMockResp: Promise<Response>;
|
||||
private addUserMockResp: Promise<Response>;
|
||||
private delUserMockResp: Promise<Response>;
|
||||
private listUsersMockResp: Promise<Response>;
|
||||
private addRoleMockResp: Promise<Response>;
|
||||
private delRoleMockResp: Promise<Response>;
|
||||
private listRolesMockResp: Promise<Response>;
|
||||
private selfMockResp: Promise<Response>;
|
||||
|
||||
constructor(url: string) {
|
||||
this.url = url;
|
||||
|
@ -25,9 +32,30 @@ export class MockUsersClient {
|
|||
setPwdMock = (resp: Promise<Response>) => {
|
||||
this.setPwdMockResp = resp;
|
||||
}
|
||||
forceSetPwdMock = (resp: Promise<Response>) => {
|
||||
this.forceSetPwdMockResp = resp;
|
||||
}
|
||||
addUserMock = (resp: Promise<Response>) => {
|
||||
this.addUserMockResp = resp;
|
||||
}
|
||||
delUserMock = (resp: Promise<Response>) => {
|
||||
this.delUserMockResp = resp;
|
||||
}
|
||||
listUsersMock = (resp: Promise<Response>) => {
|
||||
this.listUsersMockResp = resp;
|
||||
}
|
||||
addRoleMock = (resp: Promise<Response>) => {
|
||||
this.addRoleMockResp = resp;
|
||||
}
|
||||
delRoleMock = (resp: Promise<Response>) => {
|
||||
this.delRoleMockResp = resp;
|
||||
}
|
||||
listRolesMock = (resp: Promise<Response>) => {
|
||||
this.listRolesMockResp = resp;
|
||||
}
|
||||
slefMock = (resp: Promise<Response>) => {
|
||||
this.selfMockResp = resp;
|
||||
}
|
||||
|
||||
login = (user: string, pwd: string): Promise<Response> => {
|
||||
return this.loginMockResp;
|
||||
|
@ -44,9 +72,36 @@ export class MockUsersClient {
|
|||
setPwd = (oldPwd: string, newPwd: string): Promise<Response> => {
|
||||
return this.setPwdMockResp;
|
||||
}
|
||||
|
||||
forceSetPwd = (userID: string, newPwd: string): Promise<Response> => {
|
||||
return this.forceSetPwdMockResp;
|
||||
}
|
||||
|
||||
addUser = (name: string, pwd: string, role: string): Promise<Response> => {
|
||||
return this.addUserMockResp;
|
||||
}
|
||||
|
||||
delUser = (userID: string): Promise<Response> => {
|
||||
return this.delUserMockResp;
|
||||
}
|
||||
|
||||
listUsers = (): Promise<Response> => {
|
||||
return this.listUsersMockResp;
|
||||
}
|
||||
|
||||
addRole = (role: string): Promise<Response> => {
|
||||
return this.addRoleMockResp;
|
||||
}
|
||||
|
||||
delRole = (role: string): Promise<Response> => {
|
||||
return this.delRoleMockResp;
|
||||
}
|
||||
|
||||
listRoles = (): Promise<Response> => {
|
||||
return this.listRolesMockResp;
|
||||
}
|
||||
|
||||
self = (): Promise<Response> => {
|
||||
return this.selfMockResp;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,52 +259,6 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
const sizeCellClass = this.props.isVertical ? `hidden margin-s` : ``;
|
||||
const modTimeCellClass = this.props.isVertical ? `hidden margin-s` : ``;
|
||||
|
||||
const layoutChildren = [
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => this.delete()}
|
||||
className="red0-bg white-font margin-t-m margin-b-m"
|
||||
>
|
||||
Delete Selected
|
||||
</button>,
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => this.moveHere()}
|
||||
className="grey1-bg white-font margin-t-m margin-b-m"
|
||||
>
|
||||
Paste
|
||||
</button>,
|
||||
<span className="inline-block margin-t-m margin-b-m">
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.onInputChange}
|
||||
value={this.state.inputValue}
|
||||
className="black0-font margin-r-m"
|
||||
placeholder="folder name"
|
||||
/>
|
||||
<button onClick={this.onMkDir} className="grey1-bg white-font">
|
||||
Create Folder
|
||||
</button>
|
||||
</span>,
|
||||
<span className="inline-block margin-t-m margin-b-m">
|
||||
<button onClick={this.onClickUpload} className="green0-bg white-font">
|
||||
Upload Files
|
||||
</button>
|
||||
<input
|
||||
type="file"
|
||||
onChange={this.addUploads}
|
||||
multiple={true}
|
||||
value={this.props.uploadValue}
|
||||
ref={this.assignInput}
|
||||
className="black0-font hidden"
|
||||
/>
|
||||
</span>,
|
||||
];
|
||||
|
||||
// const ops = (
|
||||
// <Layouter isHorizontal={false} elements={layoutChildren}></Layouter>
|
||||
// );
|
||||
|
||||
const ops = (
|
||||
<div>
|
||||
<div>
|
||||
|
|
|
@ -124,17 +124,6 @@ export class Updater {
|
|||
: this.props.items;
|
||||
};
|
||||
|
||||
goHome = async (): Promise<void> => {
|
||||
const listResp = await this.filesClient.listHome();
|
||||
|
||||
// how to get current dir? to dirPath?
|
||||
// this.props.dirPath = dirParts;
|
||||
this.props.items =
|
||||
listResp.status === 200
|
||||
? List<MetadataResp>(listResp.data.metadatas)
|
||||
: this.props.items;
|
||||
};
|
||||
|
||||
moveHere = async (
|
||||
srcDir: string,
|
||||
dstDir: string,
|
||||
|
|
|
@ -60,16 +60,17 @@ export function initState(): ICoreState {
|
|||
uploadFiles: List<File>([]),
|
||||
},
|
||||
panes: {
|
||||
userRole: "",
|
||||
displaying: "",
|
||||
paneNames: Set<string>(["settings", "login"]),
|
||||
paneNames: Set<string>(["settings", "login", "admin"]),
|
||||
login: {
|
||||
authed: false,
|
||||
},
|
||||
admin: {
|
||||
users: Map<string, User>(),
|
||||
roles: Set<string>(),
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
users: Map<string, User>(),
|
||||
roles: Set<string>()
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -92,16 +93,17 @@ export function mockState(): ICoreState {
|
|||
uploadFiles: List<File>([]),
|
||||
},
|
||||
panes: {
|
||||
userRole: "",
|
||||
displaying: "",
|
||||
paneNames: Set<string>(["settings", "login"]),
|
||||
paneNames: Set<string>(["settings", "login", "admin"]),
|
||||
login: {
|
||||
authed: false,
|
||||
},
|
||||
admin: {
|
||||
users: Map<string, User>(),
|
||||
roles: Set<string>(),
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
users: Map<string, User>(),
|
||||
roles: Set<string>()
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,11 +2,8 @@ import * as React from "react";
|
|||
import { Map, Set } from "immutable";
|
||||
|
||||
import { ICoreState } from "./core_state";
|
||||
import { IUsersClient, User} from "../client";
|
||||
import { UsersClient } from "../client/users";
|
||||
import { User } from "../client";
|
||||
import { Updater as PanesUpdater } from "./panes";
|
||||
import { updater as BrowserUpdater } from "./browser.updater";
|
||||
import { Layouter } from "./layouter";
|
||||
|
||||
export interface Props {
|
||||
users: Map<string, User>;
|
||||
|
@ -14,165 +11,408 @@ export interface Props {
|
|||
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
|
||||
}
|
||||
|
||||
export class Updater {
|
||||
private static props: Props;
|
||||
private static client: IUsersClient;
|
||||
|
||||
static init = (props: Props) => (Updater.props = { ...props });
|
||||
|
||||
static setClient = (client: IUsersClient): void => {
|
||||
Updater.client = client;
|
||||
};
|
||||
|
||||
// static adduser = async (user: User): Promise<boolean> => {
|
||||
// const resp = await Updater.client.add
|
||||
// }
|
||||
|
||||
// static login = async (user: string, pwd: string): Promise<boolean> => {
|
||||
// const resp = await Updater.client.login(user, pwd);
|
||||
// Updater.setAuthed(resp.status === 200);
|
||||
// return resp.status === 200;
|
||||
// };
|
||||
|
||||
// static logout = async (): Promise<boolean> => {
|
||||
// const resp = await Updater.client.logout();
|
||||
// Updater.setAuthed(false);
|
||||
// return resp.status === 200;
|
||||
// };
|
||||
|
||||
// static isAuthed = async (): Promise<boolean> => {
|
||||
// const resp = await Updater.client.isAuthed();
|
||||
// return resp.status === 200;
|
||||
// };
|
||||
|
||||
// static initIsAuthed = async (): Promise<void> => {
|
||||
// return Updater.isAuthed().then((isAuthed) => {
|
||||
// Updater.setAuthed(isAuthed);
|
||||
// });
|
||||
// };
|
||||
|
||||
static setState = (preState: ICoreState): ICoreState => {
|
||||
preState.panel.authPane = {
|
||||
...preState.panel.authPane,
|
||||
...Updater.props,
|
||||
};
|
||||
return preState;
|
||||
};
|
||||
export interface UserFormProps {
|
||||
key: string;
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
roles: Set<string>;
|
||||
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
|
||||
}
|
||||
|
||||
// export interface State {
|
||||
// user: string;
|
||||
// pwd: string;
|
||||
// }
|
||||
export interface UserFormState {
|
||||
id: string;
|
||||
name: string;
|
||||
newPwd1: string;
|
||||
newPwd2: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
// export class AuthPane extends React.Component<Props, State, {}> {
|
||||
// private update: (updater: (prevState: ICoreState) => ICoreState) => void;
|
||||
// constructor(p: Props) {
|
||||
// super(p);
|
||||
// Updater.init(p);
|
||||
// Updater.setClient(new UsersClient(""));
|
||||
// this.update = p.update;
|
||||
// this.state = {
|
||||
// user: "",
|
||||
// pwd: "",
|
||||
// };
|
||||
export class UserForm extends React.Component<
|
||||
UserFormProps,
|
||||
UserFormState,
|
||||
{}
|
||||
> {
|
||||
constructor(p: UserFormProps) {
|
||||
super(p);
|
||||
this.state = {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
newPwd1: "",
|
||||
newPwd2: "",
|
||||
role: p.role,
|
||||
};
|
||||
}
|
||||
|
||||
// this.initIsAuthed();
|
||||
// }
|
||||
changePwd1 = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ newPwd1: ev.target.value });
|
||||
};
|
||||
changePwd2 = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ newPwd2: ev.target.value });
|
||||
};
|
||||
changeRole = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ role: ev.target.value });
|
||||
};
|
||||
|
||||
// changeUser = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// this.setState({ user: ev.target.value });
|
||||
// };
|
||||
setPwd = () => {
|
||||
if (this.state.newPwd1 !== this.state.newPwd2) {
|
||||
alert("2 passwords do not match, please check.");
|
||||
return;
|
||||
}
|
||||
|
||||
// changePwd = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// this.setState({ pwd: ev.target.value });
|
||||
// };
|
||||
PanesUpdater.forceSetPwd(this.state.id, this.state.newPwd1).then(
|
||||
(ok: boolean) => {
|
||||
if (ok) {
|
||||
alert("password is updated");
|
||||
} else {
|
||||
alert("failed to update password");
|
||||
}
|
||||
this.setState({
|
||||
newPwd1: "",
|
||||
newPwd2: "",
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// initIsAuthed = () => {
|
||||
// Updater.initIsAuthed().then(() => {
|
||||
// this.update(Updater.setAuthPane);
|
||||
// });
|
||||
// };
|
||||
delUser = () => {
|
||||
PanesUpdater.delUser(this.state.id)
|
||||
.then((ok: boolean) => {
|
||||
if (!ok) {
|
||||
alert("failed to delete user");
|
||||
}
|
||||
return PanesUpdater.listUsers();
|
||||
})
|
||||
.then((_: boolean) => {
|
||||
this.props.update(PanesUpdater.updateState);
|
||||
});
|
||||
};
|
||||
|
||||
// login = () => {
|
||||
// Updater.login(this.state.user, this.state.pwd)
|
||||
// .then((ok: boolean) => {
|
||||
// if (ok) {
|
||||
// this.update(Updater.setAuthPane);
|
||||
// this.setState({ user: "", pwd: "" });
|
||||
// // close all the panes
|
||||
// PanesUpdater.displayPane("");
|
||||
// this.update(PanesUpdater.updateState);
|
||||
// setRole = () => {};
|
||||
|
||||
// // refresh
|
||||
// return BrowserUpdater().setHomeItems();
|
||||
// } else {
|
||||
// this.setState({ user: "", pwd: "" });
|
||||
// alert("Failed to login.");
|
||||
// }
|
||||
// })
|
||||
// .then(() => {
|
||||
// return BrowserUpdater().refreshUploadings();
|
||||
// })
|
||||
// .then((_: boolean) => {
|
||||
// this.update(BrowserUpdater().setBrowser);
|
||||
// });
|
||||
// };
|
||||
render() {
|
||||
return (
|
||||
<span>
|
||||
<span className="flex-list-container">
|
||||
<div className="flex-list-item-l">
|
||||
<span className="vbar green0-bg"></span>
|
||||
<div
|
||||
className="margin-l-m"
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<div className="bold item-name">Name: {this.props.name}</div>
|
||||
<div className="grey1-font item-name">
|
||||
ID: {this.props.id} / Role: {this.props.role}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex-list-item-r"
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
flexBasis: "80%",
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<div className="margin-t-m">
|
||||
<button
|
||||
onClick={this.delUser}
|
||||
className="grey1-bg white-font margin-r-m"
|
||||
>
|
||||
Delete User
|
||||
</button>
|
||||
</div>
|
||||
|
||||
// logout = () => {
|
||||
// Updater.logout().then((ok: boolean) => {
|
||||
// if (ok) {
|
||||
// this.update(Updater.setAuthPane);
|
||||
// } else {
|
||||
// alert("Failed to logout.");
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
{/* no API yet */}
|
||||
{/* <div className="margin-t-m">
|
||||
<input
|
||||
name={`${this.props.id}-role`}
|
||||
type="text"
|
||||
onChange={this.changeRole}
|
||||
value={this.state.role}
|
||||
className="black0-font margin-r-m"
|
||||
placeholder={this.props.role}
|
||||
/>
|
||||
<button
|
||||
onClick={this.setRole}
|
||||
className="grey1-bg white-font margin-r-m"
|
||||
>
|
||||
Update Role
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
</span>
|
||||
|
||||
// render() {
|
||||
// const elements: Array<JSX.Element> = [
|
||||
// <input
|
||||
// name="user"
|
||||
// type="text"
|
||||
// onChange={this.changeUser}
|
||||
// value={this.state.user}
|
||||
// className="black0-font margin-t-m margin-b-m"
|
||||
// // style={{ width: "80%" }}
|
||||
// placeholder="user name"
|
||||
// />,
|
||||
// <input
|
||||
// name="pwd"
|
||||
// type="password"
|
||||
// onChange={this.changePwd}
|
||||
// value={this.state.pwd}
|
||||
// className="black0-font margin-t-m margin-b-m"
|
||||
// // style={{ width: "80%" }}
|
||||
// placeholder="password"
|
||||
// />,
|
||||
// <button
|
||||
// onClick={this.login}
|
||||
// className="green0-bg white-font margin-t-m margin-b-m"
|
||||
// >
|
||||
// Log in
|
||||
// </button>,
|
||||
// ];
|
||||
<div className="margin-t-m">
|
||||
<input
|
||||
name={`${this.props.id}-pwd1`}
|
||||
type="password"
|
||||
onChange={this.changePwd1}
|
||||
value={this.state.newPwd1}
|
||||
className="black0-font margin-r-m"
|
||||
placeholder="new password"
|
||||
/>
|
||||
<input
|
||||
name={`${this.props.id}-pwd2`}
|
||||
type="password"
|
||||
onChange={this.changePwd2}
|
||||
value={this.state.newPwd2}
|
||||
className="black0-font margin-r-m"
|
||||
placeholder="repeat password"
|
||||
/>
|
||||
<button
|
||||
onClick={this.setPwd}
|
||||
className="grey1-bg white-font margin-r-m"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
|
||||
// return (
|
||||
// <span>
|
||||
// <div
|
||||
// className="margin-l-l"
|
||||
// style={{ display: this.props.authed ? "none" : "block" }}
|
||||
// >
|
||||
// {/* <h5 className="black-font">Login</h5> */}
|
||||
// <Layouter isHorizontal={false} elements={elements} />
|
||||
// </div>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// <span style={{ display: this.props.authed ? "inherit" : "none" }}>
|
||||
// <button onClick={this.logout} className="grey1-bg white-font">
|
||||
// Log out
|
||||
// </button>
|
||||
// </span>
|
||||
// </span>
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
export interface State {
|
||||
newUserName: string;
|
||||
newUserPwd1: string;
|
||||
newUserPwd2: string;
|
||||
newUserRole: string;
|
||||
newRole: string;
|
||||
}
|
||||
export class AdminPane extends React.Component<Props, State, {}> {
|
||||
constructor(p: Props) {
|
||||
super(p);
|
||||
this.state = {
|
||||
newUserName: "",
|
||||
newUserPwd1: "",
|
||||
newUserPwd2: "",
|
||||
newUserRole: "",
|
||||
newRole: "",
|
||||
};
|
||||
}
|
||||
|
||||
onChangeUserName = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ newUserName: ev.target.value });
|
||||
};
|
||||
onChangeUserPwd1 = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ newUserPwd1: ev.target.value });
|
||||
};
|
||||
onChangeUserPwd2 = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ newUserPwd2: ev.target.value });
|
||||
};
|
||||
onChangeUserRole = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ newUserRole: ev.target.value });
|
||||
};
|
||||
onChangeRole = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ newRole: ev.target.value });
|
||||
};
|
||||
|
||||
addRole = () => {
|
||||
PanesUpdater.addRole(this.state.newRole)
|
||||
.then((ok: boolean) => {
|
||||
if (!ok) {
|
||||
alert("failed to add role");
|
||||
}
|
||||
return PanesUpdater.listRoles();
|
||||
})
|
||||
.then(() => {
|
||||
this.props.update(PanesUpdater.updateState);
|
||||
});
|
||||
};
|
||||
|
||||
delRole = (role: string) => {
|
||||
if (
|
||||
!confirm(
|
||||
"After deleting this role, some of users may not be able to login."
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
PanesUpdater.delRole(role)
|
||||
.then((ok: boolean) => {
|
||||
if (!ok) {
|
||||
alert("failed to delete role");
|
||||
}
|
||||
return PanesUpdater.listRoles();
|
||||
})
|
||||
.then(() => {
|
||||
this.props.update(PanesUpdater.updateState);
|
||||
});
|
||||
};
|
||||
|
||||
addUser = () => {
|
||||
if (this.state.newUserPwd1 !== this.state.newUserPwd2) {
|
||||
alert("2 passwords do not match, please check.");
|
||||
return;
|
||||
}
|
||||
|
||||
PanesUpdater.addUser({
|
||||
id: "", // backend will fill it
|
||||
name: this.state.newUserName,
|
||||
pwd: this.state.newUserPwd1,
|
||||
role: this.state.newUserRole,
|
||||
})
|
||||
.then((ok: boolean) => {
|
||||
if (!ok) {
|
||||
alert("failed to add user");
|
||||
}
|
||||
this.setState({
|
||||
newUserName: "",
|
||||
newUserPwd1: "",
|
||||
newUserPwd2: "",
|
||||
newUserRole: "",
|
||||
});
|
||||
return PanesUpdater.listUsers();
|
||||
})
|
||||
.then(() => {
|
||||
this.props.update(PanesUpdater.updateState);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const userList = this.props.users.valueSeq().map((user: User) => {
|
||||
return (
|
||||
<div className="margin-t-m">
|
||||
<UserForm
|
||||
key={user.id}
|
||||
id={user.id}
|
||||
name={user.name}
|
||||
role={user.role}
|
||||
roles={this.props.roles}
|
||||
update={this.props.update}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const roleList = this.props.roles.valueSeq().map((role: string) => {
|
||||
return (
|
||||
<div key={role} className="flex-list-container margin-b-m">
|
||||
<div className="flex-list-item-l">
|
||||
<span className="dot red0-bg"></span>
|
||||
<span className="bold">{role}</span>
|
||||
</div>
|
||||
<div className="flex-list-item-r">
|
||||
<button
|
||||
onClick={() => {
|
||||
this.delRole(role);
|
||||
}}
|
||||
className="grey1-bg white-font margin-r-m"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="font-size-m">
|
||||
<div className="container">
|
||||
<div className="flex-list-container padding-l">
|
||||
{/* <span className="inline-block margin-t-m margin-b-m"> */}
|
||||
<div
|
||||
className="flex-list-item-l"
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.onChangeUserName}
|
||||
value={this.state.newUserName}
|
||||
className="black0-font margin-b-m"
|
||||
placeholder="new user name"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.onChangeUserRole}
|
||||
value={this.state.newUserRole}
|
||||
className="black0-font margin-b-m"
|
||||
placeholder="new user role"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
onChange={this.onChangeUserPwd1}
|
||||
value={this.state.newUserPwd1}
|
||||
className="black0-font margin-b-m"
|
||||
placeholder="password"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
onChange={this.onChangeUserPwd2}
|
||||
value={this.state.newUserPwd2}
|
||||
className="black0-font margin-b-m"
|
||||
placeholder="repeat password"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-list-item-r">
|
||||
<button
|
||||
onClick={this.addUser}
|
||||
className="grey1-bg white-font margin-r-m"
|
||||
>
|
||||
Create User
|
||||
</button>
|
||||
</div>
|
||||
{/* </span> */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container">
|
||||
<div className="padding-l">
|
||||
<div className="flex-list-container bold">
|
||||
<span className="flex-list-item-l">
|
||||
<span className="dot black-bg"></span>
|
||||
<span>Users</span>
|
||||
</span>
|
||||
<span className="flex-list-item-r padding-r-m"></span>
|
||||
</div>
|
||||
{userList}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container">
|
||||
<div className="flex-list-container padding-l">
|
||||
<div className="flex-list-item-l">
|
||||
<span className="inline-block margin-t-m margin-b-m">
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.onChangeRole}
|
||||
value={this.state.newRole}
|
||||
className="black0-font margin-r-m"
|
||||
placeholder="new role name"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-list-item-r">
|
||||
<button
|
||||
onClick={this.addRole}
|
||||
className="grey1-bg white-font margin-r-m"
|
||||
>
|
||||
Create Role
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container">
|
||||
<div className="padding-l">
|
||||
<div className="flex-list-container bold margin-b-m">
|
||||
<span className="flex-list-item-l">
|
||||
<span className="dot black-bg"></span>
|
||||
<span>Roles</span>
|
||||
</span>
|
||||
<span className="flex-list-item-r padding-r-m"></span>
|
||||
</div>
|
||||
{roleList}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,11 +159,13 @@ export class AuthPane extends React.Component<Props, State, {}> {
|
|||
return (
|
||||
<span>
|
||||
<div
|
||||
className="margin-l-l"
|
||||
className="container"
|
||||
style={{ display: this.props.authed ? "none" : "block" }}
|
||||
>
|
||||
{/* <h5 className="black-font">Login</h5> */}
|
||||
<Layouter isHorizontal={false} elements={elements} />
|
||||
<div className="padding-l">
|
||||
{/* <h5 className="black-font">Login</h5> */}
|
||||
<Layouter isHorizontal={false} elements={elements} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span style={{ display: this.props.authed ? "inherit" : "none" }}>
|
||||
|
|
|
@ -120,58 +120,63 @@ export class PaneSettings extends React.Component<Props, State, {}> {
|
|||
];
|
||||
|
||||
return (
|
||||
<div className="padding-l">
|
||||
<div>
|
||||
<div className="flex-list-container">
|
||||
<div className="flex-list-item-l">
|
||||
<h5 className="black-font">Update Password</h5>
|
||||
<div className="container">
|
||||
<div className="padding-l">
|
||||
<div>
|
||||
<div className="flex-list-container">
|
||||
<div className="flex-list-item-l">
|
||||
<h5 className="black-font">Update Password</h5>
|
||||
</div>
|
||||
<div className="flex-list-item-r">
|
||||
<button onClick={this.setPwd} className="grey1-bg white-font">
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-list-item-r">
|
||||
<button onClick={this.setPwd} className="grey1-bg white-font">
|
||||
Update
|
||||
</button>
|
||||
|
||||
<div>
|
||||
<input
|
||||
name="old_pwd"
|
||||
type="password"
|
||||
onChange={this.changeOldPwd}
|
||||
value={this.state.oldPwd}
|
||||
className="black0-font margin-t-m margin-b-m"
|
||||
placeholder="old password"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
name="new_pwd1"
|
||||
type="password"
|
||||
onChange={this.changeNewPwd1}
|
||||
value={this.state.newPwd1}
|
||||
className="black0-font margin-t-m margin-b-m margin-r-m"
|
||||
placeholder="new password"
|
||||
/>
|
||||
<input
|
||||
name="new_pwd2"
|
||||
type="password"
|
||||
onChange={this.changeNewPwd2}
|
||||
value={this.state.newPwd2}
|
||||
className="black0-font margin-t-m margin-b-m"
|
||||
placeholder="new password again"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hr white0-bg margin-t-m margin-b-m"></div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
name="old_pwd"
|
||||
type="password"
|
||||
onChange={this.changeOldPwd}
|
||||
value={this.state.oldPwd}
|
||||
className="black0-font margin-t-m margin-b-m"
|
||||
placeholder="old password"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
name="new_pwd1"
|
||||
type="password"
|
||||
onChange={this.changeNewPwd1}
|
||||
value={this.state.newPwd1}
|
||||
className="black0-font margin-t-m margin-b-m margin-r-m"
|
||||
placeholder="new password"
|
||||
/>
|
||||
<input
|
||||
name="new_pwd2"
|
||||
type="password"
|
||||
onChange={this.changeNewPwd2}
|
||||
value={this.state.newPwd2}
|
||||
className="black0-font margin-t-m margin-b-m"
|
||||
placeholder="new password again"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hr white0-bg margin-t-m margin-b-m"></div>
|
||||
|
||||
<div>
|
||||
<div className="flex-list-container">
|
||||
<div className="flex-list-item-l">
|
||||
<h5 className="black-font">Logout</h5>
|
||||
</div>
|
||||
<div className="flex-list-item-r">
|
||||
<AuthPane authed={this.props.login.authed} update={this.update} />
|
||||
<div className="flex-list-container">
|
||||
<div className="flex-list-item-l">
|
||||
<h5 className="black-font">Logout</h5>
|
||||
</div>
|
||||
<div className="flex-list-item-r">
|
||||
<AuthPane
|
||||
authed={this.props.login.authed}
|
||||
update={this.update}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,21 +1,30 @@
|
|||
import * as React from "react";
|
||||
import { Set, Map } from "immutable";
|
||||
|
||||
import { IUsersClient, User, ListUsersResp, ListRolesResp } from "../client";
|
||||
import { UsersClient } from "../client/users";
|
||||
import { ICoreState } from "./core_state";
|
||||
import { PaneSettings } from "./pane_settings";
|
||||
import { AdminPane, Props as AdminPaneProps } from "./pane_admin";
|
||||
import { AuthPane, Props as AuthPaneProps } from "./pane_login";
|
||||
|
||||
export interface Props {
|
||||
userRole: string;
|
||||
displaying: string;
|
||||
paneNames: Set<string>;
|
||||
login: AuthPaneProps;
|
||||
admin: AdminPaneProps;
|
||||
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
|
||||
}
|
||||
|
||||
export class Updater {
|
||||
static props: Props;
|
||||
private static client: IUsersClient;
|
||||
|
||||
static init = (props: Props) => (Updater.props = { ...props });
|
||||
static setClient = (client: IUsersClient): void => {
|
||||
Updater.client = client;
|
||||
};
|
||||
|
||||
static displayPane = (paneName: string) => {
|
||||
if (paneName === "") {
|
||||
|
@ -31,7 +40,85 @@ export class Updater {
|
|||
}
|
||||
};
|
||||
|
||||
static self = async (): Promise<boolean> => {
|
||||
const resp = await Updater.client.self();
|
||||
if (resp.status === 200) {
|
||||
Updater.props.userRole = resp.data.role;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static addUser = async (user: User): Promise<boolean> => {
|
||||
const resp = await Updater.client.addUser(user.name, user.pwd, user.role);
|
||||
// TODO: should return uid instead
|
||||
return resp.status === 200;
|
||||
};
|
||||
|
||||
static delUser = async (userID: string): Promise<boolean> => {
|
||||
const resp = await Updater.client.delUser(userID);
|
||||
return resp.status === 200;
|
||||
};
|
||||
|
||||
static setRole = async (userID: string, role: string): Promise<boolean> => {
|
||||
const resp = await Updater.client.delUser(userID);
|
||||
return resp.status === 200;
|
||||
};
|
||||
|
||||
static forceSetPwd = async (
|
||||
userID: string,
|
||||
pwd: string
|
||||
): Promise<boolean> => {
|
||||
const resp = await Updater.client.forceSetPwd(userID, pwd);
|
||||
return resp.status === 200;
|
||||
};
|
||||
|
||||
static listUsers = async (): Promise<boolean> => {
|
||||
const resp = await Updater.client.listUsers();
|
||||
if (resp.status !== 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const lsRes = resp.data as ListUsersResp;
|
||||
let users = Map<User>({});
|
||||
lsRes.users.forEach((user: User) => {
|
||||
users = users.set(user.name, user);
|
||||
});
|
||||
Updater.props.admin.users = users;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
static addRole = async (role: string): Promise<boolean> => {
|
||||
const resp = await Updater.client.addRole(role);
|
||||
// TODO: should return uid instead
|
||||
return resp.status === 200;
|
||||
};
|
||||
|
||||
static delRole = async (role: string): Promise<boolean> => {
|
||||
const resp = await Updater.client.delRole(role);
|
||||
return resp.status === 200;
|
||||
};
|
||||
|
||||
static listRoles = async (): Promise<boolean> => {
|
||||
const resp = await Updater.client.listRoles();
|
||||
if (resp.status !== 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const lsRes = resp.data as ListRolesResp;
|
||||
let roles = Set<string>();
|
||||
Object.keys(lsRes.roles).forEach((role: string) => {
|
||||
roles = roles.add(role);
|
||||
});
|
||||
Updater.props.admin.roles = roles;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
static updateState = (prevState: ICoreState): ICoreState => {
|
||||
console.log(prevState, Updater.props);
|
||||
return {
|
||||
...prevState,
|
||||
panel: {
|
||||
|
@ -47,6 +134,7 @@ export class Panes extends React.Component<Props, State, {}> {
|
|||
constructor(p: Props) {
|
||||
super(p);
|
||||
Updater.init(p);
|
||||
Updater.setClient(new UsersClient(""));
|
||||
}
|
||||
|
||||
closePane = () => {
|
||||
|
@ -63,7 +151,7 @@ export class Panes extends React.Component<Props, State, {}> {
|
|||
displaying = "login";
|
||||
}
|
||||
|
||||
const panesMap: Map<string, JSX.Element> = Map({
|
||||
let panesMap: Map<string, JSX.Element> = Map({
|
||||
settings: (
|
||||
<PaneSettings login={this.props.login} update={this.props.update} />
|
||||
),
|
||||
|
@ -72,6 +160,17 @@ export class Panes extends React.Component<Props, State, {}> {
|
|||
),
|
||||
});
|
||||
|
||||
if (this.props.userRole === "admin") {
|
||||
panesMap = panesMap.set(
|
||||
"admin",
|
||||
<AdminPane
|
||||
users={this.props.admin.users}
|
||||
roles={this.props.admin.roles}
|
||||
update={this.props.update}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const panes = panesMap.keySeq().map((paneName: string): JSX.Element => {
|
||||
const isDisplay = displaying === paneName ? "" : "hidden";
|
||||
return (
|
||||
|
@ -84,24 +183,25 @@ export class Panes extends React.Component<Props, State, {}> {
|
|||
const btnClass = displaying === "login" ? "hidden" : "";
|
||||
return (
|
||||
<div id="panes" className={displaying === "" ? "hidden" : ""}>
|
||||
<div className="container">
|
||||
<div className="flex-list-container padding-l">
|
||||
<h3 className="flex-list-item-l txt-cap">{displaying}</h3>
|
||||
<div className="flex-list-item-r">
|
||||
<button
|
||||
onClick={this.closePane}
|
||||
className={`black0-bg white-font ${btnClass}`}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<div className="root-container">
|
||||
<div className="container">
|
||||
<div className="flex-list-container padding-l">
|
||||
<h3 className="flex-list-item-l txt-cap">{displaying}</h3>
|
||||
<div className="flex-list-item-r">
|
||||
<button
|
||||
onClick={this.closePane}
|
||||
className={`red0-bg white-font ${btnClass}`}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hr white0-bg margin-b-m margin-l-m margin-r-m"></div>
|
||||
{panes}
|
||||
|
||||
<div className="padding-l"></div>
|
||||
</div>
|
||||
{/* <div className="hr white0-bg margin-b-m margin-l-m margin-r-m"></div> */}
|
||||
{/* <div className="padding-l"></div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ export interface Props {
|
|||
browser: BrowserProps;
|
||||
authPane: PaneLoginProps;
|
||||
panes: PanesProps;
|
||||
admin: PaneAdminProps;
|
||||
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
|
||||
}
|
||||
|
||||
|
@ -38,15 +37,22 @@ export class RootFrame extends React.Component<Props, State, {}> {
|
|||
this.props.update(PanesUpdater.updateState);
|
||||
};
|
||||
|
||||
showAdmin = () => {
|
||||
PanesUpdater.displayPane("admin");
|
||||
this.props.update(PanesUpdater.updateState);
|
||||
};
|
||||
|
||||
render() {
|
||||
const update = this.props.update;
|
||||
return (
|
||||
<div className="theme-white desktop">
|
||||
<div id="bg" className="bg bg-img font-m">
|
||||
<Panes
|
||||
userRole={this.props.panes.userRole}
|
||||
displaying={this.props.panes.displaying}
|
||||
paneNames={this.props.panes.paneNames}
|
||||
login={this.props.authPane}
|
||||
admin={this.props.panes.admin}
|
||||
update={update}
|
||||
/>
|
||||
|
||||
|
@ -68,6 +74,12 @@ export class RootFrame extends React.Component<Props, State, {}> {
|
|||
>
|
||||
Settings
|
||||
</button>
|
||||
<button
|
||||
onClick={this.showAdmin}
|
||||
className="grey1-bg white-font margin-r-m"
|
||||
>
|
||||
Admin
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import * as React from "react";
|
||||
|
||||
import { updater as BrowserUpdater } from "./browser.updater";
|
||||
import { Updater as PanesUpdater } from "./panes";
|
||||
import { ICoreState, init } from "./core_state";
|
||||
import { RootFrame } from "./root_frame";
|
||||
import { FilesClient } from "../client/files";
|
||||
|
@ -26,6 +27,19 @@ export class StateMgr extends React.Component<Props, State, {}> {
|
|||
})
|
||||
.then((_: boolean) => {
|
||||
this.update(BrowserUpdater().setBrowser);
|
||||
})
|
||||
.then(() => {
|
||||
return PanesUpdater.self();
|
||||
})
|
||||
.then(() => {
|
||||
return PanesUpdater.listRoles();
|
||||
})
|
||||
.then((_: boolean) => {
|
||||
return PanesUpdater.listUsers();
|
||||
})
|
||||
.then((_: boolean) => {
|
||||
console.log(PanesUpdater);
|
||||
this.update(PanesUpdater.updateState);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -41,7 +55,6 @@ export class StateMgr extends React.Component<Props, State, {}> {
|
|||
update={this.update}
|
||||
browser={this.state.panel.browser}
|
||||
panes={this.state.panel.panes}
|
||||
admin={this.state.panel.admin}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue