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 && ( - <> - writeOption({ name: 'permissions', value: value.value })} - value={null} - /> - {role.permissions.size > 0 && ( -
- {role.permissions.map((p: any) => OptionLabel(permissionsMap, p, onChangePermissions))} -
- )} - - - -
-
- - {role.exists() && } -
- {role.exists() && ( - - )} -
+
+ +
+
All Projects
+ + (Uncheck to select specific projects) + +
+ {!role.allProjects && ( + <> + + writeOption({ name: 'permissions', value: value.value }) + } + value={null} + /> + {role.permissions.length > 0 && ( +
+ {role.permissions.map((p: any) => + OptionLabel(permissionsMap, p, onChangePermissions) + )} +
+ )} + + + +
+
+ + {role.exists() && } +
+ {role.exists() && ( + + )}
- ); +
+
+ ); }; -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