Display nested data using `admin-on-rest` - reactjs

I have data in the following format -
{
"categories": [
{
"id": "1",
"code": "TESTCODE001",
"name": "TESTCODE001",
"subcategories": [
{
"id": "11",
"code": "TESTCODE0002",
"name": "TESTCODE0002",
"subcategories": []
}
]
}
I am not able to display subcategories. I have tried using ReferenceArrayField but I am not able to use it, as categories and subcategories are fetched from the same API. Also tried using custom component to display subcategories, this is also not possible as it shows the edit form when I click on subcategories.
The code I have written-
categories.js
export const CategoryList = (props) => (
<List title="All categories" {...props} >
<Datagrid>
<TextField source="code" />
<TextField source="name" />
<TextField source="id" />
<LinkToRelatedSubcategories/>
<EditButton />
</Datagrid>
</List>
);
LinkToRelatedSubcategories.js
const LinkToRelatedSubcategories = ({ record, translate }) => (
<FlatButton
primary
label='Subcategories'
containerElement={<Link params={{ testvalue: "hello" }}
to={{
pathname: '/category/categoryId',
}}
/>}
/>
);
Any help will be highly appreciated.

this is also not possible as it shows the edit form when I click on subcategories.
For the show page, you can use: /category/categoryId/show
Maybe you can use react-admin instead of admin-on-rest. Its the new version, now in release candidate mode. It has https://marmelab.com/react-admin/Fields.html#arrayfield and https://marmelab.com/react-admin/Inputs.html#arrayinput

Related

How to disable add with condition in array input react admin

I have an object with a Json array as a field:
{
name: "test",
field: [
{name: "field1",value: "value1"},
{name: "field2",value: "value2"}
]
}
I am using api platform as a data provider.
I am using EditGuesser of react admin as follows:
const SetsEdit = (props:any) => (
<EditGuesser {...props}>
<InputGuesser source="name" />
<ArrayInput source="field">
<SimpleFormIterator inline disableAdd={disableAdd(props)}>
<TextInput source="name" helperText={false} />
<TextInput source="value" helperText={false} />
</SimpleFormIterator>
</ArrayInput>
</EditGuesser>
);
And I'm using the edit guesser in hydra admin
<HydraAdmin
dataProvider={dataProvider(setRedirectToLogin)}
layout={MyLayout}
loginPage={LoginPage}>
<ResourceGuesser
name="sets"
edit={SetsEdit}
/>
</HydraAdmin>
What I am trying to do is I want to disable Add when field.length is >= to 4.
So I want to add a condition to the prop disableAdd based on the value of the object.
How do I pass the object value to the function to return true or false.
You have to watch for the actual form data using react-hook-form's useWatch or react-admin's <FormDataConsumer>, and use that data in your disableAdd prop.
Something like (not tested):
const SetsEdit = (props:any) => (
<EditGuesser {...props}>
<InputGuesser source="name" />
<FormDataConsumer>
{({ formData }) => (
<ArrayInput source="field">
<SimpleFormIterator inline disableAdd={disableAdd(formData)}>
<TextInput source="name" helperText={false} />
<TextInput source="value" helperText={false} />
</SimpleFormIterator>
</ArrayInput>
)}
</FormDataConsumer>
</EditGuesser>
);

How to filter a list in react-admin with a parameter that is fetched asynchronously?

I am trying to filter a list in react-admin.
Basically, I have a list of classes, that I want to filter by teacherId. However, the teacherId has to be fetched asynchronously.
The code looks like this:
const activitiesFilters = [
<TextInput key="search" source="q" label="Search an Activity" alwaysOn />,
]
export const ActivityList = (props) => {
const teacher = useCurrentTeacherProfile() // This is the asynchronous call
return (
<List
filters={activitiesFilters}
filter={{ authorId: teacher?.id }} // Here I am using the teacher ID to filter my list
{...props}
exporter={false}
>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="title" />
<TextField source="location" />
<DateField source="dateTime" />
</Datagrid>
</List>
)
}
The above code gives me this error:
Error: ActivityList suspended while rendering, but no fallback UI was specified. Add a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.
I tried adding a <Suspense /> component above the <List /> but it doesn't work.
And if I add the <Suspense /> component at the root, above the <Admin /> one, it breaks the navigation.
Is there a way I can filter my list with a parameter that is fetched asynchronously?
Thanks!
I wonder if the error does not come from the "?." typescript operator in "teacher?.id" that resolves to undefined in JS before your async call resolves.
So I'd resolve the code as follow:
import { Loading } from 'react-admin';
const activitiesFilters = [
<TextInput key="search" source="q" label="Search an Activity" alwaysOn />,
]
export const ActivityList = (props) => {
const teacher = useCurrentTeacherProfile() // This is the asynchronous call
if (!teacher) return <Loading/>
return (
<List
filters={activitiesFilters}
filter={{ authorId: teacher?.id }} // Here I am using the teacher ID to filter my list
{...props}
exporter={false}
>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="title" />
<TextField source="location" />
<DateField source="dateTime" />
</Datagrid>
</List>
)
}

How to render videos with List in react-admin?

I have a question regarding react-admin. So I'm building this admin List which Hasura-graphQL. I am able to render Images with the ImageField component which I am using:
<ImageField label="Image" source="image" sortByOrder="DESC"/>
And which I have no problems rendering. But the issue comes when I need to render a video that comes in a URL from my graphQL schema. Something like this:
"video": "https://myappvideo.blob.core.windows.net/posts/post_93753139-524a-4c85-a0fc-d40b47bd95f5.mp4?se=2030-12-31&sp=rwdlac&sv=2018-03-28&ss=btf&srt=sco&sig=oeyHYsiWC79a1z7fcgsPPdJzeC499t%2BwPkbImcctpJE%3D",
"id": 471
},
{
"video": null,
"id": 493
},
{
"video": "https://myappvideo.blob.core.windows.net/posts/post_f9c59f2f-3d2e-4c63-ae1e-65e5324866ad.mp4?se=2030-12-31&sp=rwdlac&sv=2018-03-28&ss=btf&srt=sco&sig=oeyHYsiWC79a1z7fcgsPPdJzeC499t%2BwPkbImcctpJE%3D",
"id": 476
},[...]
How can render videos in my react-admin list? Something where I can show the videos and I can click and reproduce?
React-admin has a way to render Images but I can see something similar for videos.
Any help would be appreciated a lot!
EDIT
This is how I'm actually trying to make this work:
<Datagrid>
<TextField label="Post ID" source="id" sortByOrder="ASC" />
//I am using FileField for this, but it does not work
<FileField label="Content" source="video" rel="video" sortByOrder="ASC" />
<TextField label="Content Type" />
<UserSum source="id" />
<SimpleForm {...props} label="Flagged">
<ApproveData source="id" />
</SimpleForm>
<DateField label="Posted On" source="createdAt" showTime />
<PostListActionToolbar>
<ShowButton label="Details" color="secondary" />
<EditButton label="Archive" color="secondary" />
</PostListActionToolbar>
</Datagrid>
Ok, I just figure out how to do this. Indeed react-admin does not have a way to render videos. So in this case you'll have to create your own component.
Step 1
Is to create the function:
const VideoField = (props) => {
const record = useRecordContext(props);
return <video src={`${record.video}`} controls width="320" height="240"></video>;
}
In this case, you'll have to interpolate the video record inside the video tag. I added the controls and the width and height to add more view on the video
Step 2
You can add a default label into it like this:
VideoField.defaultProps = { label: 'Video' };
This is necessary since it will label your content on the top side of the list.
Step 3
Then add your function in to a component inside the datagrid and you'll be having something like this:
Add it like this on your datagrid
<Datagrid>
<TextField label="Post ID" source="id" sortByOrder="ASC" />
<VideoField source="video" />
<TextField label="Content Type" />
</Datagrid>

How can I render the contents of an array nested inside an object in React Admin's show/ArrayField component?

I'm at a loss as to how to render an array of objects that are nested inside a record with react-admin. The data I get back from the API looks like this:
{
"data": {
"getPromotion": {
"id": "ckfxvfrvs00033h5sz4ucoi7e",
"reference": "Monday special",
"startDate": "2020-10-06T11:20:00.000Z",
"endDate": "2020-10-13T11:20:00.000Z",
"promotionItems": {
"items": [{
"id": "ckfxxrcyg00073h5v33a27pb8",
"productId": "4286857122685",
"promotionId": "ckfxvfrvs00033h5sz4ucoi7e",
"retailerId": "ckfxvcmjf00013h5sgi4x56rp",
"discountPrice": 0.5,
"startDate": "2020-10-06T11:20:00.000Z",
"endDate": "2020-10-13T11:20:00.000Z",
"createdAt": "2020-10-06T12:25:10.072Z",
"updatedAt": "2020-10-06T12:25:10.072Z",
"owner": "xxxxx#xxxxx.com"
}],
"nextToken": null
},
"createdAt": "2020-10-06T11:20:15.749Z",
"updatedAt": "2020-10-06T11:20:15.749Z",
"owner": "xxxxx#xxxxx.com"
}
}
}
My main Show component looks like this:
export const PromotionShow = (props) => {
return (
<Show {...props}>
<SimpleShowLayout>
<TextField source="reference" label="Promotion Code" />
<DateField source="startDate" />
<DateField source="endDate" />
<PromotionItemsGrid />
</SimpleShowLayout>
</Show>
);
};
The TextField and DateFields render fine, but the PromotionItemsGrid component just shows a blank grid with no records.
The PromotionItemsGrid component looks like this:
const PromotionItemsGrid = (props) => {
console.log("props from the show component", JSON.stringify(props));
return (
<List {...props}>
<ArrayField source="props.record.promotionItems.items">
<Datagrid>
<TextField source="id" />
<TextField source="productId" />
<TextField source="retailerId" />
<TextField source="discountPrice" />
</Datagrid>
</ArrayField>
</List>
);
};
The output of the console.log indicates the component is getting all the data it needs, I just can't figure out how to pass the array to the ArrayField for the Datagrid to render.
I've tried every combination of props.record.promotionItems.items I can think of in the "source" prop of the ArrayField component, but all I ever get is a blank datagrid with no rows (but the columns specified are there). I'm reasonably confident it's a silly thing I'm missing, but I can't for the life of me work it out.
Any help gratefully received!
Thanks,
For anyone who finds this, I figured it out. I merged the two components
export const PromotionShow = (props) => {
return (
<Show {...props}>
<SimpleShowLayout>
<TextField source="reference" />
<DateField source="startDate" />
<DateField source="endDate" />
<ArrayField source="promotionItems.items" label="Items in Promotion">
<Datagrid>
<ReferenceField source="productId" label="UPC" reference="products">
<TextField source="id" />
</ReferenceField>
<ReferenceField
source="productId"
label="Product Name"
reference="products"
>
<TextField source="name" />
</ReferenceField>
<ReferenceField source="retailerId" reference="retailers">
<TextField source="name" />
</ReferenceField>
<NumberField
source="discountPrice"
/>
<DateField source="startDate" />
<DateField source="endDate" />
</Datagrid>
</ArrayField>
</SimpleShowLayout>
</Show>
);
};

How can I force sorting arrows to always display in a react-admin Datagrid?

It doesn't feel like best practice, but my users want to always see the arrows next to every column that's sortable in a list view. I'm building a web app in react, using the react-admin Datagrid object:
export const PermitList: FunctionComponent<FullProps> = (props) => {
const { hasShow, ...rest } = props;
return (
<Datagrid {...rest}>
<TextInput source="permitID" />
<TextInput source="dept" />
<TextInput source="workOrder" />
<TextInput source="status" getColor={getColorFromStatus} />
<ListButtons hasShow={hasShow!} label="Edit Permit" canDelete={false} />
</Datagrid>
);
};
Fields permitID, dept, workOrder, and status are all sortable. The sort arrow appears by default when the grid header is clicked and the field sorts, but my users want to see all sort arrows all the time. I haven't been able to find any documentation on this. Is there a way to display it using Datagrid, or do I need to use a different grid object?
You can manually add your own arrow elements using headerClassName for each sortable field:
const useListStyles = makeStyles(theme => ({
myHeader: {
"&&:before": {
content: '" ⇅ "',
color: 'red',
},
},
}))
const CardList = (props) => {
const classes = useListStyles()
return (
<List {...props} >
<Datagrid>
<TextField source="id" />
<TextField source="name" headerClassName={classes.myHeader} />
</Datagrid>
</List>
)
}

Resources