diff --git a/frontend/app/components/Client/Users/UsersView.tsx b/frontend/app/components/Client/Users/UsersView.tsx
index cb57a9ec6..5b207a468 100644
--- a/frontend/app/components/Client/Users/UsersView.tsx
+++ b/frontend/app/components/Client/Users/UsersView.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect } from 'react';
import UserList from './components/UserList';
import { PageTitle } from 'UI';
import { useStore } from 'App/mstore';
@@ -6,8 +6,15 @@ import { useObserver } from 'mobx-react-lite';
import UserSearch from './components/UserSearch';
function UsersView(props) {
- const { userStore } = useStore();
+ const { userStore, roleStore } = useStore();
const userCount = useObserver(() => userStore.list.length);
+ const roles = useObserver(() => roleStore.list);
+
+ useEffect(() => {
+ if (roles.length === 0) {
+ roleStore.fetchRoles();
+ }
+ }, []);
return (
diff --git a/frontend/app/components/Client/Users/components/UserForm/UserForm.tsx b/frontend/app/components/Client/Users/components/UserForm/UserForm.tsx
new file mode 100644
index 000000000..b47a38d50
--- /dev/null
+++ b/frontend/app/components/Client/Users/components/UserForm/UserForm.tsx
@@ -0,0 +1,129 @@
+import React from 'react';
+import { Input, CopyButton, Button, Select } from 'UI'
+import cn from 'classnames';
+import { useStore } from 'App/mstore';
+import { useObserver } from 'mobx-react-lite';
+import { useModal } from 'App/components/Modal';
+
+interface Props {
+ isSmtp?: boolean;
+ isEnterprise?: boolean;
+}
+function UserForm(props: Props) {
+ const { isSmtp = false, isEnterprise = false } = props;
+ const isSaving = false;
+ const { hideModal } = useModal();
+ const { userStore, roleStore } = useStore();
+ const user: any = useObserver(() => userStore.instance);
+ const roles = useObserver(() => roleStore.list.map(r => ({ label: r.name, value: r.roleId })));
+ console.log('roles', roles)
+
+ const onChangeCheckbox = (e: any) => {
+ user.updateKey('isAdmin', !user.isAdmin);
+ }
+
+ const onSave = () => {
+ }
+
+ const write = ({ target: { name, value } }) => {
+ user.updateKey(name, value);
+ }
+ return useObserver(() => (
+
+
+
{`${user.exists() ? 'Update' : 'Invite'} User`}
+
+
+
+
+
+
+
+
+ { !user.isJoined && user.invitationLink &&
+
+ }
+
+
+ ));
+}
+
+export default UserForm;
\ No newline at end of file
diff --git a/frontend/app/components/Client/Users/components/UserForm/index.ts b/frontend/app/components/Client/Users/components/UserForm/index.ts
new file mode 100644
index 000000000..3a390c510
--- /dev/null
+++ b/frontend/app/components/Client/Users/components/UserForm/index.ts
@@ -0,0 +1 @@
+export { default } from './UserForm';
\ No newline at end of file
diff --git a/frontend/app/components/Client/Users/components/UserList/UserList.tsx b/frontend/app/components/Client/Users/components/UserList/UserList.tsx
index 448e7c95e..e0e2e37ff 100644
--- a/frontend/app/components/Client/Users/components/UserList/UserList.tsx
+++ b/frontend/app/components/Client/Users/components/UserList/UserList.tsx
@@ -4,12 +4,15 @@ import React, { useEffect } from 'react';
import UserListItem from '../UserListItem';
import { sliceListPerPage, getRE } from 'App/utils';
import { Pagination, NoContent, Loader } from 'UI';
+import { useModal } from 'App/components/Modal';
+import UserForm from '../UserForm';
function UserList(props) {
const { userStore } = useStore();
const loading = useObserver(() => userStore.loading);
const users = useObserver(() => userStore.list);
const searchQuery = useObserver(() => userStore.searchQuery);
+ const { showModal } = useModal();
const filterList = (list) => {
const filterRE = getRE(searchQuery, 'i');
@@ -24,11 +27,18 @@ function UserList(props) {
useEffect(() => {
userStore.fetchUsers();
+ editHandler(null);
}, []);
+ const editHandler = (user) => {
+ userStore.initUser(user).then(() => {
+ showModal(
, { right: true });
+ });
+ }
+
return useObserver(() => (
-
+
Name
@@ -38,7 +48,7 @@ function UserList(props) {
{sliceListPerPage(list, userStore.page - 1, userStore.pageSize).map((user: any) => (
-
+ editHandler(user)} />
))}
diff --git a/frontend/app/components/Client/Users/components/UserListItem/UserListItem.tsx b/frontend/app/components/Client/Users/components/UserListItem/UserListItem.tsx
index 8d402ed47..3d28e8e37 100644
--- a/frontend/app/components/Client/Users/components/UserListItem/UserListItem.tsx
+++ b/frontend/app/components/Client/Users/components/UserListItem/UserListItem.tsx
@@ -1,20 +1,24 @@
import React from 'react';
+import { Icon } from 'UI';
interface Props {
user: any;
+ editHandler?: any;
}
function UserListItem(props: Props) {
- const { user } = props;
+ const { user, editHandler = () => {} } = props;
return (
-
-
{user.email}
+
+
{user.name}
{user.roleName}
-
);
diff --git a/frontend/app/mstore/index.tsx b/frontend/app/mstore/index.tsx
index dc6560c93..251b70f9e 100644
--- a/frontend/app/mstore/index.tsx
+++ b/frontend/app/mstore/index.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import DashboardStore, { IDashboardSotre } from './dashboardStore';
import MetricStore, { IMetricStore } from './metricStore';
import UserStore from './userStore';
+import RoleStore from './roleStore';
import APIClient from 'App/api_client';
import { dashboardService, metricService, sessionService, userService } from 'App/services';
import SettingsStore from './settingsStore';
@@ -11,12 +12,14 @@ export class RootStore {
metricStore: IMetricStore;
settingsStore: SettingsStore;
userStore: UserStore;
+ roleStore: RoleStore;
constructor() {
this.dashboardStore = new DashboardStore();
this.metricStore = new MetricStore();
this.settingsStore = new SettingsStore();
this.userStore = new UserStore();
+ this.roleStore = new RoleStore();
}
initClient() {
diff --git a/frontend/app/mstore/roleStore.ts b/frontend/app/mstore/roleStore.ts
new file mode 100644
index 000000000..6f87b4bcb
--- /dev/null
+++ b/frontend/app/mstore/roleStore.ts
@@ -0,0 +1,31 @@
+import { makeAutoObservable, observable, action } from "mobx"
+import { userService } from "App/services";
+import Role, { IRole } from "./types/role";
+
+export default class UserStore {
+ list: IRole[] = [];
+ loading: boolean = false;
+
+ constructor() {
+ makeAutoObservable(this, {
+ list: observable,
+ loading: observable,
+ })
+ }
+
+ fetchRoles(): Promise
{
+ this.loading = true;
+ return new Promise((resolve, reject) => {
+ userService.getRoles()
+ .then(response => {
+ this.list = response.map((role: any) => new Role().fromJson(role));
+ resolve(response);
+ }).catch(error => {
+ this.loading = false;
+ reject(error);
+ }).finally(() => {
+ this.loading = false;
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/frontend/app/mstore/types/role.ts b/frontend/app/mstore/types/role.ts
new file mode 100644
index 000000000..189984e0f
--- /dev/null
+++ b/frontend/app/mstore/types/role.ts
@@ -0,0 +1,42 @@
+import { makeAutoObservable, observable, runInAction } from "mobx";
+
+export interface IRole {
+ roleId: string;
+ name: string;
+ description: string;
+
+ fromJson(json: any): IRole;
+ toJson(): any;
+}
+
+export default class Role implements IRole {
+ roleId: string = '';
+ name: string = '';
+ description: string = '';
+
+
+ constructor() {
+ makeAutoObservable(this, {
+ roleId: observable,
+ name: observable,
+ description: observable,
+ })
+ }
+
+ fromJson(json: any) {
+ runInAction(() => {
+ this.roleId = json.id;
+ this.name = json.name;
+ this.description = json.description;
+ })
+ return this;
+ }
+
+ toJson() {
+ return {
+ id: this.roleId,
+ name: this.name,
+ description: this.description,
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/app/mstore/types/user.ts b/frontend/app/mstore/types/user.ts
index 88b168624..557beb794 100644
--- a/frontend/app/mstore/types/user.ts
+++ b/frontend/app/mstore/types/user.ts
@@ -1,5 +1,7 @@
import { runInAction, makeAutoObservable, observable } from 'mobx'
import { DateTime } from 'luxon';
+import { validateEmail } from 'App/validate';
+
export interface IUser {
userId: string
email: string
@@ -16,8 +18,9 @@ export interface IUser {
toJson(): any
}
-export default class User {
+export default class User implements IUser {
userId: string = '';
+ name: string = '';
email: string = '';
createdAt: string = '';
isAdmin: boolean = false;
@@ -43,9 +46,17 @@ export default class User {
})
}
+ updateKey(key: string, value: any) {
+ console.log(key, value)
+ runInAction(() => {
+ this[key] = value
+ })
+ }
+
fromJson(json: any) {
runInAction(() => {
this.userId = json.id;
+ this.name = json.name;
this.email = json.email;
this.createdAt = json.createdAt && DateTime.fromISO(json.createdAt || 0)
this.isAdmin = json.admin
@@ -67,4 +78,12 @@ export default class User {
roleId: this.roleId,
}
}
+
+ valid() {
+ return validateEmail(this.email) && !!this.roleId;
+ }
+
+ exists() {
+ return !!this.userId;
+ }
}
\ No newline at end of file
diff --git a/frontend/app/mstore/userStore.ts b/frontend/app/mstore/userStore.ts
index 170055412..33e2e9754 100644
--- a/frontend/app/mstore/userStore.ts
+++ b/frontend/app/mstore/userStore.ts
@@ -15,6 +15,18 @@ export default class UserStore {
instance: observable,
updateUser: action,
updateKey: action,
+ initUser: action,
+ })
+ }
+
+ initUser(user?: any ): Promise {
+ return new Promise((resolve, reject) => {
+ if (user) {
+ this.instance = user;
+ } else {
+ this.instance = new User();
+ }
+ resolve();
})
}
diff --git a/frontend/app/services/UserService.ts b/frontend/app/services/UserService.ts
index 3cdead002..ac65a9647 100644
--- a/frontend/app/services/UserService.ts
+++ b/frontend/app/services/UserService.ts
@@ -42,4 +42,10 @@ export default class UserService {
.then(response => response.json())
.then(response => response.data || {});
}
+
+ getRoles() {
+ return this.client.get('/client/roles')
+ .then(response => response.json())
+ .then(response => response.data || []);
+ }
}
\ No newline at end of file
diff --git a/frontend/app/styles/main.css b/frontend/app/styles/main.css
index 4258e43d7..3b7f5fe4b 100644
--- a/frontend/app/styles/main.css
+++ b/frontend/app/styles/main.css
@@ -147,13 +147,4 @@
height: 100vh;
overflow-y: hidden;
padding-right: 15px;
-}
-
-/* .svg-map__location {
- fill: #EEE !important;
- cursor: pointer;
-
- &:hover {
- fill: #fff !important;
- }
-} */
\ No newline at end of file
+}
\ No newline at end of file