Authorization to access resource based on role and record's state - react-admin

What is the best way to restrict access to resource depend on both the user's permission and the state of the record? For example, form that has some kind of workflow such as timesheet or expense claim system, where user can submit a form and then the form is in submitted state where user can only view them and only the admin can edit the form?
Trying to figure out best way to enforce the permissions based in AuthProvider to prevent user to just change the URL browser from switching between "show/edit" mode.

I see 2 possibilities:
Set both a show and an edit view in the Resource. In the edit view, if the user permissions don't allow the edition, use a <Redirect> component to redirect to the show view.
Set only an edit view that, depending on the permissions, renders a <SimpleForm> or a <SimpleShowLayout>
Here is how I would go with the second solution:
import * as React from 'react'
import { Edit, useRecordContext, SimpleForm, TextInput, SimpleShowLayout, TextField } from 'react-admin';
export const PostEdit = ({ permissions, ...props }) => (
<Edit {...props}>
<EditContent permissions={permissions}/>
</Edit>
);
const EditContent = ({ permissions }) => {
const record = useRecordContext();
if (record?.canEdit && permissions === "admin") {
return (
<SimpleForm>
<TextInput source="title" />
</SimpleForm>
);
} else {
return (
<SimpleShowLayout>
<TextField source="title" />
</SimpleShowLayout>);
}
};

Related

React Native Screen refresh from nested component screen

I have a screen, PDP, that screen contains a component, TopNews. I want TopNews onclick to redraw PDP (it passes in an Article ID which the PDP uses to retrieve the article). The diagram below shows the flow
The code I have to support this inside TopNews is;
<TouchableOpacity onPress={() => navigation.navigate('Pdp', {articleid: item.id})}>
The challenge is the TouchableOpacity event triggers, but the page doesn't refresh the PDP. I don't want to refresh only the PDP as I may include the TopNews component in other screens outside of the PDP, its just in this case its inside of the screen it needs to call.
There are more then one way to do it.
You can create a state for the article id and allow the TopNews to set this state:
const PDP = () => {
const [ id, setId ] = React.useState(0);
return (
<div>
<ArticleComponent articleId={id} />
<TopNews idSetter={setId} />
</div>
);
}
const ArticleComponent = ({ articleId }) => {
return /* something that renders the article by the id */ ;
}
const TopNews = ({ idSetter }) => {
return (
<TouchableOpacity
onPress={() => {
idSetter(item.id);
navigation.navigate('Pdp')
}}>
);
}
Another more elegant way, is to use react contexts. You can create a context provider in the PDP component and change the context data in any PDP's children component.
I found the answer, instead of using navigation.navigate, use navigation.push instead;
<TouchableOpacity onPress={() => navigation.push('Pdp', {articleid: item.id})}>
This works perfectly :)

React-Admin <SimpleForm> component doesn't trigger the "UPDATE" action in the data provider

So I'm using the ra-data-json-server as a data provider and the bult in <SimpleForm> component as a form for the <Edit> view, and I'm facing a pretty strange issue, as it says in documentation, when submitted, the <SimpleForm> forces data provider to make a PUT request to the API, but in my case it does not.
Here's my Edit view compnent:
export const UserEdit = props => {
return (
<Edit {...props}>
<SimpleForm>
<ArrayInput source="applications">
<SimpleFormIterator source="applications">
{/* some inputs */}
</SimpleFormIterator>
</ArrayInput>
</SimpleForm>
</Edit>
);
};
And the admin component itself:
export const AdminComp = () => {
return (
<Admin
loginPage={CustomLoginPage}
authProvider={authProvider}
locale="ru"
i18nProvider={i18nProvider}
dataProvider={dataProvider}
>
<Resource
name="students"
list={UserList}
edit={UserEdit}
/>
</Admin>
);
};
And everytime I get into the Edit view and hit the save button it just doesn't do anything.
I managed to make it at least call the update in data provider, but it would call it with the old form data even though it was modified already.
I also tried reinstalling react-admin to the latest version which is what some people reccomended but it didn't help.

How can I customize the filter form onChange behaviour in the list view of react-admin

The default behaviour of the filters in the list view of react-admin is to filter the list and cause an API call (presumably submit the form) on every change to the filter inputs. I would like an API call only to happen when an input onBlurs or when an explicit 'Search' button is clicked.
I have been playing with the onChange, onBlur functions of the inputs and the Filter, preventDefault, setFilters ... Nothing has come close to working but I include some template code below.
Perhaps I'd like to be able to specify the redux-form touchOnChange and touchOnBlur props but don't see any way of doing that.
How can this be achieved?
import { Filter, TextInput } from react-admin;
import { Button } from '#material-ui/core';
const MyFilter = (props) => {
return ( <Filter {...props} onChange={()=>{}}>
<TextInput label="Search" source="q" alwaysOn onBlur={()=>{}}/>
<Button alwaysOn onClick={(event)=>(props && props.setFilters(props.filterValues))}>SEARCH</Button>
<TextInput source="name"/>
</Filter> )
}

React-Admin: How to redirect to a specified `list` after hitting 'save' on a custom route

I have a custom route:
<Route
exact
path="/assetsBulkCreate"
component={ComponentWithPermissions(AssetsBulkCreate)}
/>
It is used to create assets in bulk:
export const AssetsBulkCreate = ({permissions, ...props}) => {
return (
<Create
resource="assets/bulkInsert"
...
This works.
However, after we hit save the page is redirected to the Dashboard.
I would like to redirect it to the list of the resource assets (which is a different resource).
How can this be done?
P.S. The redirect prop does not let us specify a different resource, so I cannot use list as value there (it does not even work because the custom route does not have a list).
The redirect prop also accept a function. See the documentation
For example:
const redirect = (basePath, id, data) => `/author/${data.author_id}/show`;
export const PostEdit = (props) => {
<Edit {...props}>
<SimpleForm redirect={redirect}>
...
</SimpleForm>
</Edit>
);
You can redirect to the list view. See documentation
export const PostCreate = (props) => {
<Create {...props}>
<SimpleForm redirect="list">
...
</SimpleForm>
</Edit>
);

react native component button not enabling

import React from 'react';
import {
View,
Text,
Button,
TextInput,
StyleSheet
} from 'react-native';
const username = null;
const SetupForm = ({onSubmit}) => {
this.handleUsernameInput = (text) => {
username = text;
}
this.handleSetupSubmit = (event) => {
onSubmit(event, username);
}
this.handleSkipSubmit = (event) => {
onSubmit(event, false);
}
return (
<View>
<Text>Enter username:</Text>
<TextInput
placeholder="Username"
maxLength={20}
onSubmitEditing={this.handleSetupSubmit}
onChangeText={this.handleUsernameInput}
style={{ marginLeft: 20, marginRight: 20 }}
/>
<View>
<Button
title="Select"
onPress={this.handleSetupSubmit}
color="#34A853"
disabled={username === null}
/>
<Button
title="Maybe Later"
onPress={this.handleSkipSubmit}
color="red"
/>
</View>
</View>
);
}
export default SetupForm;
I have an issue with this Setup Form. it features a username text box I am setting to the value the user enters. onChangeText={this.handleUsernameInput}
for the select button I have set disabled when username === null however this never becomes false when entering text into the text box.
the text box is set to set the username when typing and I can see in the console its working correctly when I press the submit via the keyboard button with the redux action
{type: "SETUP.REQUEST", username: "aaaaaaa"}
is there something I am doing wrong here that the button is not becoming enabled when the text is getting set by the this.handleUsernameInput method?
example gif:
http://i.imgur.com/OuMkxHA.gifv
I have to click the skip button after typing in a username to make the button enable (go green), I want this to enable whilst typing
Like mentioned in the Garrett's comment the component doesn't re-render because there's no prop or state change when you change the text input. You should change the functional component to a ES6 class and save the username into the state. There's an simple example of this in the TextInput's documention
I'm not sure about your skill level but React's documentation about state and lifecycle is a good read if you are new to React https://facebook.github.io/react/docs/state-and-lifecycle.html