change(ui) - users list

This commit is contained in:
Shekar Siri 2022-05-04 12:27:44 +02:00
parent 0bbd27e856
commit 7d08e32d25
13 changed files with 361 additions and 3 deletions

View file

@ -7,6 +7,7 @@ import { fetchList as fetchMemberList } from 'Duck/member';
import ProfileSettings from './ProfileSettings';
import Integrations from './Integrations';
import ManageUsers from './ManageUsers';
import UserView from './Users/UsersView';
import Sites from './Sites';
import CustomFields from './CustomFields';
import Webhooks from './Webhooks';
@ -36,7 +37,7 @@ export default class Client extends React.PureComponent {
<Switch>
<Route exact strict path={ clientRoute(CLIENT_TABS.PROFILE) } component={ ProfileSettings } />
<Route exact strict path={ clientRoute(CLIENT_TABS.INTEGRATIONS) } component={ Integrations } />
<Route exact strict path={ clientRoute(CLIENT_TABS.MANAGE_USERS) } component={ ManageUsers } />
<Route exact strict path={ clientRoute(CLIENT_TABS.MANAGE_USERS) } component={ UserView } />
<Route exact strict path={ clientRoute(CLIENT_TABS.SITES) } component={ Sites } />
<Route exact strict path={ clientRoute(CLIENT_TABS.CUSTOM_FIELDS) } component={ CustomFields } />
<Route exact strict path={ clientRoute(CLIENT_TABS.WEBHOOKS) } component={ Webhooks } />

View file

@ -0,0 +1,30 @@
import React from 'react';
import UserList from './components/UserList';
import { PageTitle } from 'UI';
import { useStore } from 'App/mstore';
import { useObserver } from 'mobx-react-lite';
import UserSearch from './components/UserSearch';
function UsersView(props) {
const { userStore } = useStore();
const userCount = useObserver(() => userStore.list.length);
return (
<div>
<div className="flex items-center justify-between">
<PageTitle
title={<div>Team <span className="color-gray-medium">{userCount}</span></div>}
actionButton={(
<div className="ml-2">test</div>
)}
/>
<div>
<UserSearch />
</div>
</div>
<UserList />
</div>
);
}
export default UsersView;

View file

@ -0,0 +1,47 @@
import { useStore } from 'App/mstore';
import { useObserver } from 'mobx-react-lite';
import React, { useEffect } from 'react';
import UserListItem from '../UserListItem';
import { sliceListPerPage } from 'App/utils';
import { Pagination } from 'UI';
function UserList(props) {
const { userStore } = useStore();
const list = useObserver(() => userStore.list);
const length = list.length;
useEffect(() => {
userStore.fetchUsers();
}, []);
return useObserver(() => (
<div>
<div className="mt-3 rounded bg-white">
<div className="grid grid-cols-12 p-3 font-medium">
<div className="col-span-5">Name</div>
<div className="col-span-3">Role</div>
<div className="col-span-"></div>
</div>
{sliceListPerPage(list, userStore.page - 1, userStore.pageSize).map((user: any) => (
<div key={user.id} className="">
<UserListItem user={user} />
</div>
))}
</div>
<div className="w-full flex items-center justify-center py-6">
<Pagination
page={userStore.page}
totalPages={Math.ceil(length / userStore.pageSize)}
onPageChange={(page) => userStore.updateKey('page', page)}
limit={userStore.pageSize}
debounceRequest={100}
/>
</div>
</div>
));
}
export default UserList;

View file

@ -0,0 +1 @@
export { default } from './UserList'

View file

@ -0,0 +1,23 @@
import React from 'react';
interface Props {
user: any;
}
function UserListItem(props: Props) {
const { user } = props;
return (
<div className="grid grid-cols-12 p-3 border-t select-none hover:bg-active-blue">
<div className="col-span-5">{user.email}</div>
<div className="col-span-3">
<span className="px-2 py-1 bg-gray-lightest rounded border color-teal text-sm capitalize">
{user.roleName}
</span>
</div>
<div className="col-span-3">
</div>
</div>
);
}
export default UserListItem;

View file

@ -0,0 +1 @@
export { default } from './UserListItem';

View file

@ -0,0 +1,35 @@
import { useObserver } from 'mobx-react-lite';
import React, { useEffect, useState } from 'react';
import { useStore } from 'App/mstore';
import { Icon } from 'UI';
import { debounce } from 'App/utils';
let debounceUpdate: any = () => {}
function UserSearch(props) {
const { userStore } = useStore();
const [query, setQuery] = useState(userStore.searchQuery);
useEffect(() => {
debounceUpdate = debounce((key, value) => userStore.updateKey(key, value), 500);
}, [])
const write = ({ target: { name, value } }) => {
setQuery(value);
debounceUpdate(name, value);
}
return useObserver(() => (
<div className="relative" style={{ width: '300px'}}>
<Icon name="search" className="absolute top-0 bottom-0 ml-3 m-auto" size="16" />
<input
value={query}
name="searchQuery"
className="bg-white p-2 border border-gray-light rounded w-full pl-10"
placeholder="Filter by Name, Project"
onChange={write}
/>
</div>
));
}
export default UserSearch;

View file

@ -0,0 +1 @@
export { default } from './UserSearch';

View file

@ -1,19 +1,22 @@
import React from 'react';
import DashboardStore, { IDashboardSotre } from './dashboardStore';
import MetricStore, { IMetricStore } from './metricStore';
import UserStore from './userStore';
import APIClient from 'App/api_client';
import { dashboardService, metricService, sessionService } from 'App/services';
import { dashboardService, metricService, sessionService, userService } from 'App/services';
import SettingsStore from './settingsStore';
export class RootStore {
dashboardStore: IDashboardSotre;
metricStore: IMetricStore;
settingsStore: SettingsStore;
userStore: UserStore;
constructor() {
this.dashboardStore = new DashboardStore();
this.metricStore = new MetricStore();
this.settingsStore = new SettingsStore();
this.userStore = new UserStore();
}
initClient() {
@ -21,6 +24,7 @@ export class RootStore {
dashboardService.initClient(client)
metricService.initClient(client)
sessionService.initClient(client)
userService.initClient(client)
}
}

View file

@ -0,0 +1,70 @@
import { runInAction, makeAutoObservable, observable } from 'mobx'
import { DateTime } from 'luxon';
export interface IUser {
userId: string
email: string
createdAt: string
isAdmin: boolean
isSuperAdmin: boolean
isJoined: boolean
isExpiredInvite: boolean
roleId: string
roleName: string
invitationLink: string
fromJson(json: any): IUser
toJson(): any
}
export default class User {
userId: string = '';
email: string = '';
createdAt: string = '';
isAdmin: boolean = false;
isSuperAdmin: boolean = false;
isJoined: boolean = false;
isExpiredInvite: boolean = false;
roleId: string = '';
roleName: string = '';
invitationLink: string = '';
constructor() {
makeAutoObservable(this, {
userId: observable,
email: observable,
createdAt: observable,
isAdmin: observable,
isSuperAdmin: observable,
isJoined: observable,
isExpiredInvite: observable,
roleId: observable,
roleName: observable,
invitationLink: observable,
})
}
fromJson(json: any) {
runInAction(() => {
this.userId = json.id;
this.email = json.email;
this.createdAt = json.createdAt && DateTime.fromISO(json.createdAt || 0)
this.isAdmin = json.admin
this.isSuperAdmin = json.superAdmin
this.isJoined = json.joined
this.isExpiredInvite = json.expiredInvitation
this.roleId = json.roleId
this.roleName = json.roleName
this.invitationLink = json.invitationLink
})
return this;
}
toJson() {
return {
email: this.email,
isAdmin: this.isAdmin,
isSuperAdmin: this.isSuperAdmin,
roleId: this.roleId,
}
}
}

View file

@ -0,0 +1,98 @@
import { makeAutoObservable, observable, action } from "mobx"
import User, { IUser } from "./types/user";
import { userService } from "App/services";
export default class UserStore {
list: IUser[] = [];
instance: IUser|null = null;
loading: boolean = false;
page: number = 1;
pageSize: number = 5;
searchQuery: string = "";
constructor() {
makeAutoObservable(this, {
instance: observable,
updateUser: action,
updateKey: action,
})
}
updateKey(key: string, value: any) {
this[key] = value
}
updateUser(user: any) {
Object.assign(this.instance, user);
}
fetchUser(userId: string): Promise<any> {
this.loading = true;
return new Promise((resolve, reject) => {
userService.one(userId)
.then(response => {
this.instance = new User().fromJson(response.data);
resolve(response);
}).catch(error => {
this.loading = false;
reject(error);
}).finally(() => {
this.loading = false;
});
});
}
fetchUsers(): Promise<any> {
this.loading = true;
return new Promise((resolve, reject) => {
userService.all()
.then(response => {
console.log('list', response);
this.list = response.map(user => new User().fromJson(user));
resolve(response);
}).catch(error => {
this.loading = false;
reject(error);
}).finally(() => {
this.loading = false;
});
});
}
saveUser(user: IUser): Promise<any> {
this.loading = true;
const wasCreating = !user.userId;
return new Promise((resolve, reject) => {
userService.save(user)
.then(response => {
if (wasCreating) {
this.list.push(new User().fromJson(response.data));
} else {
this.updateUser(response.data);
}
resolve(response);
}).catch(error => {
this.loading = false;
reject(error);
}).finally(() => {
this.loading = false;
});
});
}
deleteUser(userId: string): Promise<any> {
this.loading = true;
return new Promise((resolve, reject) => {
userService.delete(userId)
.then(response => {
this.instance = null;
resolve(response);
}).catch(error => {
this.loading = false;
reject(error);
}).finally(() => {
this.loading = false;
});
});
}
}

View file

@ -0,0 +1,45 @@
import APIClient from 'App/api_client';
import { IUser } from 'App/mstore/types/user'
export default class UserService {
private client: APIClient;
constructor(client?: APIClient) {
this.client = client ? client : new APIClient();
}
initClient(client?: APIClient) {
this.client = client || new APIClient();
}
all() {
return this.client.get('/client/members')
.then(response => response.json())
.then(response => response.data || []);
}
one(userId: string) {
return this.client.get('/users/' + userId)
.then(response => response.json())
.then(response => response.data || {});
}
save(user: IUser) {
const data = user.toJson();
if (user.userId) {
return this.client.put('/users/' + user.userId, data)
.then(response => response.json())
.then(response => response.data || {});
} else {
return this.client.post('/users', data)
.then(response => response.json())
.then(response => response.data || {});
}
}
delete(userId: string) {
return this.client.delete('/users/' + userId)
.then(response => response.json())
.then(response => response.data || {});
}
}

View file

@ -1,7 +1,9 @@
import DashboardService, { IDashboardService } from "./DashboardService";
import MetricService, { IMetricService } from "./MetricService";
import SessionSerivce from "./SessionService";
import UserService from "./UserService";
export const dashboardService: IDashboardService = new DashboardService();
export const metricService: IMetricService = new MetricService();
export const sessionService: SessionSerivce = new SessionSerivce();
export const sessionService: SessionSerivce = new SessionSerivce();
export const userService: UserService = new UserService();