diff --git a/frontend/app/api_client.ts b/frontend/app/api_client.ts
index 570efbd66..2a9394687 100644
--- a/frontend/app/api_client.ts
+++ b/frontend/app/api_client.ts
@@ -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',
diff --git a/frontend/app/components/Client/Roles/Roles.tsx b/frontend/app/components/Client/Roles/Roles.tsx
index 470cd611b..a5ef62264 100644
--- a/frontend/app/components/Client/Roles/Roles.tsx
+++ b/frontend/app/components/Client/Roles/Roles.tsx
@@ -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(, { 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)));
diff --git a/frontend/app/components/Client/Roles/components/Permissions/Permissions.tsx b/frontend/app/components/Client/Roles/components/Permissions/Permissions.tsx
deleted file mode 100644
index 0dd56dfd9..000000000
--- a/frontend/app/components/Client/Roles/components/Permissions/Permissions.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react';
-import Role from 'Types/role'
-
-interface Props {
- role: Role
-}
-function Permissions(props: Props) {
- return (
-
-
-
- );
-}
-
-export default Permissions;
\ No newline at end of file
diff --git a/frontend/app/components/Client/Roles/components/Permissions/index.ts b/frontend/app/components/Client/Roles/components/Permissions/index.ts
deleted file mode 100644
index 659544a53..000000000
--- a/frontend/app/components/Client/Roles/components/Permissions/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './Permissions';
\ No newline at end of file
diff --git a/frontend/app/components/Client/Roles/components/RoleForm/RoleForm.tsx b/frontend/app/components/Client/Roles/components/RoleForm/RoleForm.tsx
index b6b9efe92..b8287dd42 100644
--- a/frontend/app/components/Client/Roles/components/RoleForm/RoleForm.tsx
+++ b/frontend/app/components/Client/Roles/components/RoleForm/RoleForm.tsx
@@ -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;
- closeModal: (toastMessage?: string) => void;
- saving: boolean;
- permissions: Array[];
- projectOptions: Array[];
- permissionsMap: any;
- projectsMap: any;
- deleteHandler: (id: any) => Promise;
+ closeModal: (toastMessage?: string) => void;
+ projects: any[];
+ permissionsMap: any;
+ deleteHandler: (id: any) => Promise;
}
const RoleForm = (props: Props) => {
- const { role, edit, save, closeModal, saving, permissions, projectOptions, permissionsMap, projectsMap } = props;
- let focusElement = useRef(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(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 (
-
-
{role.exists() ? 'Edit Role' : 'Create Role'}
-
-
-
-
-
+ useEffect(() => {
+ focusElement && focusElement.current && focusElement.current.focus();
+ }, []);
-
-
+ return (
+
+
+ {role.exists() ? 'Edit Role' : 'Create Role'}
+
+
+
+
+
+
-
-
-
-
All Projects
-
(Uncheck to select specific projects)
-
-
- {!role.allProjects && (
- <>
-
+
+ );
};
-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 (
-
-
{nameMap[p]}
-
onChangeOption(p)}>
-
-
-
- );
+ return (
+
+
{nameMap[p]}
+
onChangeOption(p)}>
+
+
+
+ );
}
diff --git a/frontend/app/duck/index.ts b/frontend/app/duck/index.ts
index 7c9b15eb5..dbf727d52 100644
--- a/frontend/app/duck/index.ts
+++ b/frontend/app/duck/index.ts
@@ -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,
diff --git a/frontend/app/duck/roles.js b/frontend/app/duck/roles.js
deleted file mode 100644
index d51d18f1f..000000000
--- a/frontend/app/duck/roles.js
+++ /dev/null
@@ -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;
diff --git a/frontend/app/duck/siteSlice.ts b/frontend/app/duck/siteSlice.ts
new file mode 100644
index 000000000..d59893a90
--- /dev/null
+++ b/frontend/app/duck/siteSlice.ts
@@ -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) => {
+ state.instance = action.payload;
+ },
+ editGDPR(state, action: PayloadAction) {
+ state.instance = {
+ ...state.instance!,
+ gdpr: action.payload,
+ }
+ },
+ setSiteId(state, action: PayloadAction) {
+ 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
+ };
+}
\ No newline at end of file
diff --git a/frontend/app/mstore/roleStore.ts b/frontend/app/mstore/roleStore.ts
index 45c8b65d0..3631105ef 100644
--- a/frontend/app/mstore/roleStore.ts
+++ b/frontend/app/mstore/roleStore.ts
@@ -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) => {
+ Object.assign(this.instance, role)
+ }
+
+ saveRole = async (role: Role): Promise => {
+ if (role.roleId) {
+ return this.modifyRole(role);
+ } else {
+ return this.createRole(role);
+ }
+ }
+
+ deleteRole = async (role: Role): Promise => {
+ 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 => {
+ 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 => {
+ 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 => {
+ 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 {
- 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)
});
- }
-}
\ No newline at end of file
+ });
+ }
+}
diff --git a/frontend/app/mstore/types/role.ts b/frontend/app/mstore/types/role.ts
index 041648192..30acfced9 100644
--- a/frontend/app/mstore/types/role.ts
+++ b/frontend/app/mstore/types/role.ts
@@ -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,
+ };
+ }
}
diff --git a/frontend/app/services/UserService.ts b/frontend/app/services/UserService.ts
index 89bffd3fc..03bd7c146 100644
--- a/frontend/app/services/UserService.ts
+++ b/frontend/app/services/UserService.ts
@@ -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())
diff --git a/frontend/app/types/role.js b/frontend/app/types/role.js
deleted file mode 100644
index d63afa5db..000000000
--- a/frontend/app/types/role.js
+++ /dev/null
@@ -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),
- }
- },
-});
diff --git a/frontend/app/types/site/site.ts b/frontend/app/types/site/site.ts
index 47d68b84e..0767541a8 100644
--- a/frontend/app/types/site/site.ts
+++ b/frontend/app/types/site/site.ts
@@ -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 {
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 : '',
});
}
\ No newline at end of file