ui: migrating duck/roles to mobx

This commit is contained in:
nick-delirium 2024-09-10 13:42:25 +02:00
parent 98e50d0e96
commit 0f89770560
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
13 changed files with 522 additions and 370 deletions

View file

@ -59,7 +59,7 @@ export default class APIClient {
constructor() {
const jwt = store.getState().getIn(['user', 'jwt']);
const siteId = store.getState().site.siteId;
const siteId = store.getState().getIn(['site', 'siteId']);
this.init = {
headers: new Headers({
Accept: 'application/json',

View file

@ -4,12 +4,12 @@ import { Loader, NoContent, Button, Tooltip } from 'UI';
import { connect } from 'react-redux';
import stl from './roles.module.css';
import RoleForm from './components/RoleForm';
import { init, edit, fetchList, remove as deleteRole, resetErrors } from 'Duck/roles';
import RoleItem from './components/RoleItem';
import { confirm } from 'UI';
import { toast } from 'react-toastify';
import withPageTitle from 'HOCs/withPageTitle';
import { useModal } from 'App/components/Modal';
import { useStore } from "App/mstore";
import { observer } from 'mobx-react-lite';
interface Props {
loading: boolean;
@ -27,25 +27,23 @@ interface Props {
}
function Roles(props: Props) {
const { loading, roles, init, edit, deleteRole, account, permissionsMap, projectsMap, removeErrors } = props;
const { roleStore } = useStore();
const roles = roleStore.list;
const loading = roleStore.loading;
const init = roleStore.init;
const deleteRole = roleStore.deleteRole;
const permissionsMap: any = {};
roleStore.permissions.forEach((p: any) => {
permissionsMap[p.value] = p.text;
});
const { account, projectsMap } = props;
const { showModal, hideModal } = useModal();
const isAdmin = account.admin || account.superAdmin;
useEffect(() => {
props.fetchList();
void roleStore.fetchRoles();
}, []);
useEffect(() => {
if (removeErrors && removeErrors.size > 0) {
removeErrors.forEach((e: any) => {
toast.error(e);
});
}
return () => {
props.resetErrors();
};
}, [removeErrors]);
const editHandler = (role: any) => {
init(role);
showModal(<RoleForm closeModal={hideModal} permissionsMap={permissionsMap} deleteHandler={deleteHandler} />, { right: true });
@ -110,24 +108,13 @@ function Roles(props: Props) {
export default connect(
(state: any) => {
const permissions = state.getIn(['roles', 'permissions']);
const permissionsMap: any = {};
permissions.forEach((p: any) => {
permissionsMap[p.value] = p.text;
});
const projects = state.getIn(['site', 'list']);
return {
instance: state.getIn(['roles', 'instance']) || null,
permissionsMap: permissionsMap,
roles: state.getIn(['roles', 'list']),
removeErrors: state.getIn(['roles', 'removeRequest', 'errors']),
loading: state.getIn(['roles', 'fetchRequest', 'loading']),
account: state.getIn(['user', 'account']),
projectsMap: projects.reduce((acc: any, p: any) => {
acc[p.get('id')] = p.get('name');
acc[p.id] = p.name;
return acc;
}, {}),
};
},
{ init, edit, fetchList, deleteRole, resetErrors }
)(withPageTitle('Roles & Access - OpenReplay Preferences')(Roles));
}
)(withPageTitle('Roles & Access - OpenReplay Preferences')(observer(Roles)));

View file

@ -1,15 +0,0 @@
import React from 'react';
import Role from 'Types/role'
interface Props {
role: Role
}
function Permissions(props: Props) {
return (
<div>
</div>
);
}
export default Permissions;

View file

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

View file

@ -1,196 +1,239 @@
import React, { useRef, useEffect } from 'react';
import { observer } from 'mobx-react-lite';
import React, { useEffect, useRef } from 'react';
import { connect } from 'react-redux';
import stl from './roleForm.module.css';
import { save, edit } from 'Duck/roles';
import { Form, Input, Button, Checkbox, Icon } from 'UI';
import { useStore } from 'App/mstore';
import { Button, Checkbox, Form, Icon, Input } from 'UI';
import Select from 'Shared/Select';
import stl from './roleForm.module.css';
interface Permission {
name: string;
value: string;
name: string;
value: string;
}
interface Props {
role: any;
edit: (role: any) => void;
save: (role: any) => Promise<void>;
closeModal: (toastMessage?: string) => void;
saving: boolean;
permissions: Array<Permission>[];
projectOptions: Array<any>[];
permissionsMap: any;
projectsMap: any;
deleteHandler: (id: any) => Promise<void>;
closeModal: (toastMessage?: string) => void;
projects: any[];
permissionsMap: any;
deleteHandler: (id: any) => Promise<void>;
}
const RoleForm = (props: Props) => {
const { role, edit, save, closeModal, saving, permissions, projectOptions, permissionsMap, projectsMap } = props;
let focusElement = useRef<any>(null);
const _save = () => {
save(role).then(() => {
closeModal(role.exists() ? 'Role updated' : 'Role created');
});
};
const { roleStore } = useStore();
const role = roleStore.instance;
const saving = roleStore.loading;
const { closeModal, permissionsMap, projects } = props;
const projectOptions = projects
.filter(({ value }) => !role.projects.includes(value))
.map((p: any) => ({
key: p.id,
value: p.id,
label: p.name,
}))
.filter(({ value }: any) => !role.projects.includes(value));
const projectsMap = projects.reduce((acc: any, p: any) => {
acc[p.id] = p.name;
return acc;
}, {});
const write = ({ target: { value, name } }: any) => edit({ [name]: value });
let focusElement = useRef<any>(null);
const permissions: {}[] = roleStore.permissions
.filter(({ value }) => !role.permissions.includes(value))
.map((p) => ({
label: p.text,
value: p.value,
}));
const _save = () => {
roleStore.saveRole(role).then(() => {
closeModal(role.exists() ? 'Role updated' : 'Role created');
});
};
const onChangePermissions = (e: any) => {
const { permissions } = role;
const index = permissions.indexOf(e);
const _perms = permissions.contains(e) ? permissions.remove(index) : permissions.push(e);
edit({ permissions: _perms });
};
const write = ({ target: { value, name } }: any) => roleStore.editRole({ [name]: value });
const onChangeProjects = (e: any) => {
const { projects } = role;
const index = projects.indexOf(e);
const _projects = index === -1 ? projects.push(e) : projects.remove(index);
edit({ projects: _projects });
};
const onChangePermissions = (e: any) => {
const { permissions } = role;
const index = permissions.indexOf(e);
let _perms;
if (permissions.includes(e)) {
permissions.splice(index, 1);
_perms = permissions;
} else {
_perms = permissions.concat(e);
}
roleStore.editRole({ permissions: _perms });
};
const writeOption = ({ name, value }: any) => {
if (name === 'permissions') {
onChangePermissions(value);
} else if (name === 'projects') {
onChangeProjects(value);
}
};
const onChangeProjects = (e: any) => {
const { projects } = role;
const index = projects.indexOf(e);
let _projects;
if (index === -1) {
_projects = projects.concat(e)
} else {
projects.splice(index, 1)
_projects = projects
}
roleStore.editRole({ projects: _projects });
};
const toggleAllProjects = () => {
const { allProjects } = role;
edit({ allProjects: !allProjects });
};
const writeOption = ({ name, value }: any) => {
if (name === 'permissions') {
onChangePermissions(value);
} else if (name === 'projects') {
onChangeProjects(value);
}
};
useEffect(() => {
focusElement && focusElement.current && focusElement.current.focus();
}, []);
const toggleAllProjects = () => {
const { allProjects } = role;
roleStore.editRole({ allProjects: !allProjects });
};
return (
<div className="bg-white h-screen overflow-y-auto" style={{ width: '350px' }}>
<h3 className="p-5 text-2xl">{role.exists() ? 'Edit Role' : 'Create Role'}</h3>
<div className="px-5">
<Form onSubmit={_save}>
<Form.Field>
<label>{'Title'}</label>
<Input
ref={focusElement}
name="name"
value={role.name}
onChange={write}
maxLength={40}
className={stl.input}
id="name-field"
placeholder="Ex. Admin"
/>
</Form.Field>
useEffect(() => {
focusElement && focusElement.current && focusElement.current.focus();
}, []);
<Form.Field>
<label>{'Project Access'}</label>
return (
<div
className="bg-white h-screen overflow-y-auto"
style={{ width: '350px' }}
>
<h3 className="p-5 text-2xl">
{role.exists() ? 'Edit Role' : 'Create Role'}
</h3>
<div className="px-5">
<Form onSubmit={_save}>
<Form.Field>
<label>{'Title'}</label>
<Input
ref={focusElement}
name="name"
value={role.name}
onChange={write}
maxLength={40}
className={stl.input}
id="name-field"
placeholder="Ex. Admin"
/>
</Form.Field>
<div className="flex my-3">
<Checkbox
name="allProjects"
className="font-medium mr-3"
type="checkbox"
checked={role.allProjects}
onClick={toggleAllProjects}
label={''}
/>
<div className="cursor-pointer leading-none select-none" onClick={toggleAllProjects}>
<div>All Projects</div>
<span className="text-xs text-gray-600">(Uncheck to select specific projects)</span>
</div>
</div>
{!role.allProjects && (
<>
<Select
isSearchable
name="projects"
options={projectOptions}
onChange={({ value }: any) => writeOption({ name: 'projects', value: value.value })}
value={null}
/>
{role.projects.size > 0 && (
<div className="flex flex-row items-start flex-wrap mt-4">
{role.projects.map((p: any) => OptionLabel(projectsMap, p, onChangeProjects))}
</div>
)}
</>
)}
</Form.Field>
<Form.Field>
<label>{'Project Access'}</label>
<Form.Field>
<label>{'Capability Access'}</label>
<Select
isSearchable
name="permissions"
options={permissions}
onChange={({ value }: any) => writeOption({ name: 'permissions', value: value.value })}
value={null}
/>
{role.permissions.size > 0 && (
<div className="flex flex-row items-start flex-wrap mt-4">
{role.permissions.map((p: any) => OptionLabel(permissionsMap, p, onChangePermissions))}
</div>
)}
</Form.Field>
</Form>
<div className="flex items-center">
<div className="flex items-center mr-auto">
<Button onClick={_save} disabled={!role.validate()} loading={saving} variant="primary" className="float-left mr-2">
{role.exists() ? 'Update' : 'Add'}
</Button>
{role.exists() && <Button onClick={closeModal}>{'Cancel'}</Button>}
</div>
{role.exists() && (
<Button variant="text" onClick={() => props.deleteHandler(role)}>
<Icon name="trash" size="18" />
</Button>
)}
</div>
<div className="flex my-3">
<Checkbox
name="allProjects"
className="font-medium mr-3"
type="checkbox"
checked={role.allProjects}
onClick={toggleAllProjects}
label={''}
/>
<div
className="cursor-pointer leading-none select-none"
onClick={toggleAllProjects}
>
<div>All Projects</div>
<span className="text-xs text-gray-600">
(Uncheck to select specific projects)
</span>
</div>
</div>
{!role.allProjects && (
<>
<Select
isSearchable
name="projects"
options={projectOptions}
onChange={({ value }: any) =>
writeOption({ name: 'projects', value: value.value })
}
value={null}
/>
{role.projects.size > 0 && (
<div className="flex flex-row items-start flex-wrap mt-4">
{role.projects.map((p: any) =>
OptionLabel(projectsMap, p, onChangeProjects)
)}
</div>
)}
</>
)}
</Form.Field>
<Form.Field>
<label>{'Capability Access'}</label>
<Select
isSearchable
name="permissions"
options={permissions}
onChange={({ value }: any) =>
writeOption({ name: 'permissions', value: value.value })
}
value={null}
/>
{role.permissions.length > 0 && (
<div className="flex flex-row items-start flex-wrap mt-4">
{role.permissions.map((p: any) =>
OptionLabel(permissionsMap, p, onChangePermissions)
)}
</div>
)}
</Form.Field>
</Form>
<div className="flex items-center">
<div className="flex items-center mr-auto">
<Button
onClick={_save}
disabled={!role.validate}
loading={saving}
variant="primary"
className="float-left mr-2"
>
{role.exists() ? 'Update' : 'Add'}
</Button>
{role.exists() && <Button onClick={closeModal}>{'Cancel'}</Button>}
</div>
{role.exists() && (
<Button variant="text" onClick={() => props.deleteHandler(role)}>
<Icon name="trash" size="18" />
</Button>
)}
</div>
);
</div>
</div>
);
};
export default connect(
(state: any) => {
const role = state.getIn(['roles', 'instance']);
const projects = state.getIn(['site', 'list']);
return {
role,
projectOptions: projects
.map((p: any) => ({
key: p.get('id'),
value: p.get('id'),
label: p.get('name'),
// isDisabled: role.projects.includes(p.get('id')),
}))
.filter(({ value }: any) => !role.projects.includes(value))
.toJS(),
permissions: state
.getIn(['roles', 'permissions'])
.filter(({ value }: any) => !role.permissions.includes(value))
.map(({ text, value }: any) => ({ label: text, value }))
.toJS(),
saving: state.getIn(['roles', 'saveRequest', 'loading']),
projectsMap: projects.reduce((acc: any, p: any) => {
acc[p.get('id')] = p.get('name');
return acc;
}, {}),
};
},
{ edit, save }
)(RoleForm);
export default connect((state: any) => {
const projects = state.getIn(['site', 'list']);
return {
projects,
};
})(observer(RoleForm));
function OptionLabel(nameMap: any, p: any, onChangeOption: (e: any) => void) {
return (
<div className="px-2 py-1 rounded bg-gray-lightest mr-2 mb-2 border flex items-center justify-between" key={p.roleId}>
<div>{nameMap[p]}</div>
<div className="cursor-pointer ml-2" onClick={() => onChangeOption(p)}>
<Icon name="close" size="12" />
</div>
</div>
);
return (
<div
className="px-2 py-1 rounded bg-gray-lightest mr-2 mb-2 border flex items-center justify-between"
key={p.roleId}
>
<div>{nameMap[p]}</div>
<div className="cursor-pointer ml-2" onClick={() => onChangeOption(p)}>
<Icon name="close" size="12" />
</div>
</div>
);
}

View file

@ -14,7 +14,6 @@ import customFields from './customField';
import integrations from './integrations';
import errors from './errors';
import funnels from './funnels';
import roles from './roles';
import customMetrics from './customMetrics';
import search from './search';
import liveSearch from './liveSearch';
@ -31,7 +30,6 @@ const rootReducer = combineReducers({
customFields,
errors,
funnels,
roles,
customMetrics,
search,
liveSearch,

View file

@ -1,62 +0,0 @@
import { List, Map } from 'immutable';
import Role from 'Types/role';
import crudDuckGenerator from './tools/crudDuck';
import { reduceDucks } from 'Duck/tools';
import { createListUpdater } from './funcTools/tools';
const crudDuck = crudDuckGenerator('client/role', Role, { idKey: 'roleId' });
export const { fetchList, init, edit, remove } = crudDuck.actions;
const RESET_ERRORS = 'roles/RESET_ERRORS';
const initialState = Map({
list: List(),
permissions: List([
{ text: 'Session Replay', value: 'SESSION_REPLAY' },
{ text: 'Developer Tools', value: 'DEV_TOOLS' },
// { text: 'Errors', value: 'ERRORS' },
{ text: 'Dashboard', value: 'METRICS' },
{ text: 'Assist (Live)', value: 'ASSIST_LIVE' },
{ text: 'Assist (Call)', value: 'ASSIST_CALL' },
{ text: 'Feature Flags', value: 'FEATURE_FLAGS' },
{ text: 'Spots', value: "SPOT" },
{ text: 'Change Spot Visibility', value: 'SPOT_PUBLIC' }
]),
});
// const name = "role";
const idKey = 'roleId';
const updateItemInList = createListUpdater(idKey);
const updateInstance = (state, instance) =>
state.getIn(['instance', idKey]) === instance[idKey]
? state.mergeIn(['instance'], instance)
: state;
const reducer = (state = initialState, action = {}) => {
switch (action.type) {
case RESET_ERRORS:
return state.setIn(['removeRequest', 'errors'], null);
case crudDuck.actionTypes.SAVE.SUCCESS:
return updateItemInList(updateInstance(state, action.data), Role(action.data));
}
return state;
};
export function save(instance) {
return {
types: crudDuck.actionTypes.SAVE.toArray(),
call: (client) =>
instance.roleId
? client.post(`/client/roles/${instance.roleId}`, instance.toData())
: client.put(`/client/roles`, instance.toData()),
};
}
export function resetErrors() {
return {
type: RESET_ERRORS,
};
}
export default reduceDucks(crudDuck, { initialState, reducer }).reducer;

View file

@ -0,0 +1,129 @@
import { PayloadAction, createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import Site, { ISite } from "Types/site";
import GDPR, { IGDPR } from 'Types/site/gdpr';
import { apiClient } from 'App/api_client';
import { GLOBAL_HAS_NO_RECORDINGS, SITE_ID_STORAGE_KEY } from "../constants/storageKeys";
import { array } from "./funcTools/tools";
const storedSiteId = localStorage.getItem(SITE_ID_STORAGE_KEY);
interface SiteState {
list: ISite[];
instance: ISite | null;
remainingSites?: number;
siteId?: number;
active: ISite | null;
}
const initialState: SiteState = {
list: [],
instance: null,
remainingSites: undefined,
siteId: undefined,
active: null,
};
const siteSlice = createSlice({
name: 'site',
initialState,
reducers: {
init: (state, action: PayloadAction<ISite>) => {
state.instance = action.payload;
},
editGDPR(state, action: PayloadAction<IGDPR>) {
state.instance = {
...state.instance!,
gdpr: action.payload,
}
},
setSiteId(state, action: PayloadAction<string>) {
const siteId = action.payload;
const site = state.list.find((s) => s.id === parseInt(siteId));
if (site) {
state.siteId = siteId;
state.active = site;
localStorage.setItem(SITE_ID_STORAGE_KEY, siteId);
}
},
updateProjectRecordingStatus(state, action: PayloadAction<{ siteId: string; status: boolean }>) {
const { siteId, status } = action.payload;
const site = state.list.find((s) => s.id === parseInt(siteId));
if (site) {
site.recorded = status;
}
},
fetchGDPRSuccess(state, action: { data: IGDPR }) {
state.instance = {
...state.instance!,
gdpr: GDPR(action.data),
}
},
saveSiteSuccess(state, action: { data: ISite }) {
const newSite = Site(action.data);
state.siteId = newSite.id;
state.instance = newSite;
state.active = newSite;
},
saveGDPRSuccess(state, action: { data: IGDPR }) {
const gdpr = GDPR(action.data);
state.instance = {
...state.instance!,
gdpr: gdpr,
}
},
fetchListSuccess(state, action: { data: ISite[], siteIdFromPath: number }) {
const siteId = state.siteId;
const ids = action.data.map(s => parseInt(s.projectId))
const siteExists = ids.includes(parseInt(siteId!));
if (action.siteIdFromPath && ids.includes(parseInt(action.siteIdFromPath))) {
state.siteId = action.siteIdFromPath;
} else if (!siteId || !siteExists) {
state.siteId = ids.includes(parseInt(storedSiteId!))
? storedSiteId
: action.data[0].projectId;
}
const list = action.data.map(Site);
const hasRecordings = list.some(s => s.recorded);
if (!hasRecordings) {
localStorage.setItem(GLOBAL_HAS_NO_RECORDINGS, 'true');
} else {
localStorage.removeItem(GLOBAL_HAS_NO_RECORDINGS);
}
state.list = list;
state.active = list.find(s => parseInt(s.id) === parseInt(state.siteId!));
}
},
})
export function save(site: ISite) {
return {
types: ['sites/saveSiteRequest', 'sites/saveSiteSuccess', 'sites/saveSiteFail'],
call: (client) => client.post(`/projects`, site),
};
}
export function fetchGDPR(siteId: number) {
return {
types: ['sites/fetchGDPRRequest', 'sites/fetchGDPRSuccess', 'sites/fetchGDPRFail'],
call: (client) => client.get(`/${siteId}/gdpr`),
};
}
export const saveGDPR = (siteId: number) => (dispatch, getState) => {
const g = getState().site.instance.gdpr;
return dispatch({
types: ['sites/saveGDPRRequest', 'sites/saveGDPRSuccess', 'sites/saveGDPRFail'],
call: client => client.post(`/${siteId}/gdpr`, g)
});
};
export function fetchList(siteId: number) {
return {
types: ['sites/fetchListRequest', 'sites/fetchListSuccess', 'sites/fetchListFail'],
call: client => client.get('/projects'),
siteIdFromPath: siteId
};
}

View file

@ -1,31 +1,109 @@
import { makeAutoObservable, observable } from "mobx"
import { userService } from "App/services";
import Role, { IRole } from "./types/role";
import { makeAutoObservable } from 'mobx';
import { userService } from 'App/services';
import Role from './types/role';
const permissions = [
{ text: 'Session Replay', value: 'SESSION_REPLAY' },
{ text: 'Developer Tools', value: 'DEV_TOOLS' },
{ text: 'Dashboard', value: 'METRICS' },
{ text: 'Assist (Live)', value: 'ASSIST_LIVE' },
{ text: 'Assist (Call)', value: 'ASSIST_CALL' },
{ text: 'Feature Flags', value: 'FEATURE_FLAGS' },
{ text: 'Spots', value: "SPOT" },
{ text: 'Change Spot Visibility', value: 'SPOT_PUBLIC' }
]
export default class UserStore {
list: IRole[] = [];
loading: boolean = false;
list: Role[] = [];
loading: boolean = false;
permissions = permissions;
instance = new Role();
constructor() {
makeAutoObservable(this, {
list: observable,
loading: observable,
constructor() {
makeAutoObservable(this);
}
toggleLoading = (val: boolean) => {
this.loading = val;
}
setRoles = (roles: Role[]) => {
this.list = roles;
}
init = (role?: any) => {
if (role) {
this.instance = new Role().fromJson(role);
} else {
this.instance = new Role();
}
}
editRole = (role: Partial<Role>) => {
Object.assign(this.instance, role)
}
saveRole = async (role: Role): Promise<void> => {
if (role.roleId) {
return this.modifyRole(role);
} else {
return this.createRole(role);
}
}
deleteRole = async (role: Role): Promise<void> => {
this.toggleLoading(true)
try {
const { data } = await userService.deleteRole(role.roleId);
this.setRoles(data.map((role: any) => new Role().fromJson(role)));
} catch (e) {
console.error(e)
} finally {
this.toggleLoading(false)
}
}
createRole = async (role: Role): Promise<void> => {
this.toggleLoading(true)
try {
const { data } = await userService.createRole(role);
this.setRoles([...this.list, new Role().fromJson(data)]);
} catch (e) {
console.error(e)
} finally {
this.toggleLoading(false)
}
}
modifyRole = async (role: Role): Promise<void> => {
this.toggleLoading(true)
try {
const { data } = await userService.modifyRole(role);
this.setRoles(this.list.map((r) => r.roleId === data.roleId ? new Role().fromJson(data) : r));
} catch (e) {
console.error(e)
} finally {
this.toggleLoading(false)
}
}
fetchRoles = (): Promise<any> => {
this.toggleLoading(true)
return new Promise((resolve, reject) => {
userService
.getRoles()
.then((response) => {
this.setRoles(response.map((role: any) => new Role().fromJson(role)))
resolve(response);
})
}
fetchRoles(): Promise<any> {
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;
});
.catch((error) => {
reject(error);
})
.finally(() => {
this.toggleLoading(false)
});
}
}
});
}
}

View file

@ -1,36 +1,49 @@
import { makeAutoObservable, observable, runInAction } from "mobx";
import { makeAutoObservable, runInAction } from 'mobx';
import { notEmptyString, validateName } from 'App/validate';
export default class Role {
roleId: string = '';
name: string = '';
description: string = '';
isProtected: boolean = false;
roleId: string = '';
name: string = '';
description: string = '';
permissions: string[] = [];
createdAt: number = 0;
isProtected: boolean = false;
serviceRole: boolean = false;
allProjects = false;
projects: string[] = [];
protected = false;
constructor() {
makeAutoObservable(this);
}
constructor() {
makeAutoObservable(this, {
roleId: observable,
name: observable,
description: observable,
})
}
fromJson(json: any) {
runInAction(() => {
Object.assign(this, json);
});
return this;
}
fromJson(json: any) {
runInAction(() => {
this.roleId = json.roleId;
this.name = json.name;
this.description = json.description;
this.isProtected = json.protected;
})
return this;
}
get validate() {
return (
notEmptyString(this.name) &&
validateName(this.name, { diacritics: true }) &&
(this.allProjects || this.projects.length > 0)
);
};
toJson() {
return {
id: this.roleId,
name: this.name,
description: this.description,
}
}
exists() {
return Boolean(this.roleId);
}
toJson() {
return {
id: this.roleId,
name: this.name,
description: this.description,
permissions: this.permissions,
allProjects: this.allProjects,
};
}
}

View file

@ -58,6 +58,21 @@ export default class UserService {
.then((response: { data: any; }) => response.data || []);
}
createRole(role: any) {
return this.client.post('/client/roles/', role)
.then(r => r.json())
}
modifyRole(role: any) {
return this.client.put(`/client/roles/${role.roleId}`, role)
.then(r => r.json())
}
deleteRole(roleId: string) {
return this.client.delete(`/client/roles/${roleId}`)
.then(r => r.json())
}
getLimits() {
return this.client.get('/limits')
.then((response: { json: () => any; }) => response.json())

View file

@ -1,34 +0,0 @@
import Record from 'Types/Record';
import { notEmptyString, validateName } from 'App/validate';
import { List } from 'immutable';
export default Record({
roleId: undefined,
name: '',
allProjects: true,
permissions: List(),
projects: List(),
protected: false,
description: '',
permissionOptions: List(),
}, {
idKey: 'roleId',
methods: {
validate() {
return notEmptyString(this.name) && validateName(this.name, { diacritics: true }) && (this.allProjects || this.projects.size > 0);
},
toData() {
const js = this.toJS();
delete js.key;
delete js.protected;
return js;
},
},
fromJS({ projects = [], permissions = [], ...rest }) {
return {
...rest,
permissions: List(permissions),
projects: List(projects),
}
},
});

View file

@ -1,7 +1,7 @@
import GDPR, { IGDPR } from './gdpr';
export interface ISite {
id?: number;
id?: string;
name: string;
host: string;
platform: string;
@ -37,7 +37,8 @@ export default function Site(data: Partial<ISite>): ISite {
return Object.assign({}, defaults, {
...data,
gdpr: GDPR(data.gdpr),
host: data.name,
id: data?.projectId?.toString(),
gdpr: GDPR(data ? data.gdpr : undefined),
host: data ? data.name : '',
});
}