I'm building an admin interface with react admin on top of firestore.
Everything works great with the firestore dataProvider, but now I would like to handle permissions and, based on permissions, show a list of experts.
To achieve this I decided to implement a custom Admin
with AdminContext
and AdminUI
.
The expected behaviour of my current implementation: if I'm not logged in, I'm first redirected to the login page and then based on permissions (usePermission
), I decide what to render.
The real behaviour: AsyncResources gets the render even if not logged in and it will crash on the permissions check because is null.
function App() {
return (
<AdminContext authProvider={authProvider} dataProvider={dataProvider}>
<AsyncResources />
</AdminContext>
);
}
Resources based on auth user permissions
function AsyncResources() {
const [emails, setEmails] = useState<string[]>([]);
const { loading, permissions } = usePermissions();
useEffect(() => {
(async () => {
const experts = await firebase.firestore().collection("experts").get();
const docIds: string[] = experts.docs.map((doc) => doc.id);
setEmails(docIds);
})();
}, []);
if (loading) {
return (
<AdminUI layout={CustomLayout} theme={theme}>
<Loading
loadingPrimary="WePractice Admin UI"
loadingSecondary="Loading..."
/>
</AdminUI>
);
}
if (permissions.admin) {
return (
<AdminUI layout={CustomLayout} theme={theme}>
{emails.map((email) => (
<Resource
key={email}
options={{ label: `${email}` }}
name={`experts/${email}/requests`}
list={RequestList}
edit={RequestEdit}
/>
))}
<Resource name={`experts`} list={ExpertList} />
</AdminUI>
);
}
return (
<AdminUI layout={CustomLayout} theme={theme}>
<Resource
options={{ label: `${permissions.email}` }}
name={`experts/${permissions.email}/requests`}
list={RequestList}
edit={RequestEdit}
/>
</AdminUI>
);
}
A solution to fix this is probably to to the following:
<AdminContext authProvider={authProvider} dataProvider={dataProvider}>
<AdminUI layout={CustomLayout} theme={theme}>
<AsyncResources />
</AdminUI>
</AdminContext>
In this case seems the AdminUI
to do the authentication check and redirect to login.
The problem with this solution is that Resources
cannot be wrapped by div
, fragments
or other components (I return an array), so in this case anything would be rendered.
Anyone has a solution to this?
Thanks
I solved my problem by moving the permissions logic at the upper level and using the Admin component.
New implementation
function App() {
const [emails, setEmails] = useState<string[]>([]);
useEffect(() => {
(async () => {
const experts = await firebase.firestore().collection("experts").get();
const docIds: string[] = experts.docs.map((doc) => doc.id);
setEmails(docIds);
})();
}, []);
return (
<Admin
catchAll={NotFound}
authProvider={authProvider}
dataProvider={dataProvider}
layout={CustomLayout}
theme={theme}
>
{(props) => {
if (props.admin) {
return [
emails.map((email) => (
<Resource
key={email}
options={{ label: `${email}` }}
name={`experts/${email}/requests`}
list={RequestList}
edit={RequestEdit}
/>
)),
<Resource name={`experts`} list={ExpertList} />,
];
}
return [
<Resource
options={{ label: `${props.email}` }}
name={`experts/${props.email}/requests`}
list={RequestList}
edit={RequestEdit}
/>,
];
}}
</Admin>
);
}