Two resources in a single page

I want to create a single page with two different resource tables on it. Is it possible? How should I need to configure my Route on App.tsx? <Route path="/devices" element={ <DevicessListPage> <Outlet /> </DevicessListPage> } > <Route index element={null} /> <Route path="show/:id" element={<DriverShowPage />} /> <Route path="create" element={ <DriverCreatePage> <Outlet /> </DriverCreatePage> } > </Route> </Route>
17 Replies
kapa.ai
kapa.ai2mo ago
Hi @Valugar. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Yes, it is possible to have two different resource tables on a single page in Refine. You can achieve this by defining two different resources and their respective routes in your App.tsx file. Here is an example of how you can define your routes:
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { Refine } from "@refinedev/core";
import dataProvider from "@refinedev/simple-rest";
import routerProvider from "@refinedev/react-router-v6";

import { DevicesListPage, DriverShowPage, DriverCreatePage } from "./pages";

function App() {
return (
<BrowserRouter>
<Refine
dataProvider={dataProvider}
routerProvider={routerProvider}
resources={[
{
name: "devices",
list: "/devices",
},
{
name: "drivers",
list: "/drivers",
show: "/drivers/show/:id",
create: "/drivers/create",
},
]}
>
<Routes>
<Route path="/devices" element={<DevicesListPage />} />
<Route path="/drivers">
<Route index element={null} />
<Route path="show/:id" element={<DriverShowPage />} />
<Route path="create" element={<DriverCreatePage />} />
</Route>
</Routes>
</Refine>
</BrowserRouter>
);
}

export default App;
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { Refine } from "@refinedev/core";
import dataProvider from "@refinedev/simple-rest";
import routerProvider from "@refinedev/react-router-v6";

import { DevicesListPage, DriverShowPage, DriverCreatePage } from "./pages";

function App() {
return (
<BrowserRouter>
<Refine
dataProvider={dataProvider}
routerProvider={routerProvider}
resources={[
{
name: "devices",
list: "/devices",
},
{
name: "drivers",
list: "/drivers",
show: "/drivers/show/:id",
create: "/drivers/create",
},
]}
>
<Routes>
<Route path="/devices" element={<DevicesListPage />} />
<Route path="/drivers">
<Route index element={null} />
<Route path="show/:id" element={<DriverShowPage />} />
<Route path="create" element={<DriverCreatePage />} />
</Route>
</Routes>
</Refine>
</BrowserRouter>
);
}

export default App;
In this example, DevicesListPage and DriverShowPage or DriverCreatePage are your custom components where you can use Refine's hooks and components to fetch and display data from your API. Please note that the list, show, and create properties in the resources array are used to define the paths for the respective CRUD operations. For more information, you can refer to the following documentation: - Resources (https://refine.dev/docs/api-reference/core/components/refine-config/#resources) - React Router v6 (https://refine.dev/docs/packages/documentation/routers/react-router-v6) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
fascinating-indigo
fascinating-indigo2mo ago
@kapa.ai Not exactly how I want it, So I have gateways and dashcameras, and I want to have both under a single 'devices'. import type { IResourceItem } from "@refinedev/core"; import { CarOutlined, DashboardOutlined, TeamOutlined} from "@ant-design/icons"; export const resources: IResourceItem[] = [ { name: "dashboard", list: "/", meta: { label: "Dashboard", icon: <DashboardOutlined />, } }, { name: "vehicles", list: "/vehicles", create: "/vehicles/create", edit: "/vehicles/edit/:id", show: "/vehicles/show/:id", meta: { canDelete: true, icon: <CarOutlined/> }, }, { name: "drivers", list: "/drivers", create: "/drivers/create", edit: "/drivers/edit/:id", show: "/drivers/show/:id", meta: { label: "Drivers", icon: <TeamOutlined />, }, }, { name: "gateways", list: "/gateways", create: "/gateways/create", edit: "/gateways/edit/:id", show: "/gateways/show/:id", meta: { label: "Devices", icon: <TeamOutlined />, }, }, { name: "dashcameras", list: "/dashcameras", create: "/dashcameras/create", edit: "/dashcameras/edit/:id", show: "/dashcameras/show/:id", }, ]; How can it be done? Do I need to configure a single resource for both? Or configure two resources and use both on the same page?
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- You can create a single page that displays both gateways and dashcameras by creating a custom page and using the <List> component from Refine for each resource. First, define your resources as you have done:
export const resources: IResourceItem[] = [
// ... other resources
{
name: "gateways",
list: "/devices",
create: "/gateways/create",
edit: "/gateways/edit/:id",
show: "/gateways/show/:id",
meta: {
label: "Devices",
icon: <TeamOutlined />,
},
},
{
name: "dashcameras",
list: "/devices",
create: "/dashcameras/create",
edit: "/dashcameras/edit/:id",
show: "/dashcameras/show/:id",
},
];
export const resources: IResourceItem[] = [
// ... other resources
{
name: "gateways",
list: "/devices",
create: "/gateways/create",
edit: "/gateways/edit/:id",
show: "/gateways/show/:id",
meta: {
label: "Devices",
icon: <TeamOutlined />,
},
},
{
name: "dashcameras",
list: "/devices",
create: "/dashcameras/create",
edit: "/dashcameras/edit/:id",
show: "/dashcameras/show/:id",
},
];
Then, create a custom page that uses the <List> component for each resource:
import { List } from "@refinedev/antd";

const DevicesPage: React.FC = () => {
return (
<div>
<h1>Gateways</h1>
<List resource="gateways">
{/* Rest of your page here */}
</List>

<h1>Dash Cameras</h1>
<List resource="dashcameras">
{/* Rest of your page here */}
</List>
</div>
);
};
import { List } from "@refinedev/antd";

const DevicesPage: React.FC = () => {
return (
<div>
<h1>Gateways</h1>
<List resource="gateways">
{/* Rest of your page here */}
</List>

<h1>Dash Cameras</h1>
<List resource="dashcameras">
{/* Rest of your page here */}
</List>
</div>
);
};
In this example, DevicesPage is your custom component where you can use Refine's hooks and components to fetch and display data from your API. The resource prop in the <List> component specifies which resource's data to fetch and display. For more information, you can refer to the following documentation: - List Component (https://refine.dev/docs/ui-integrations/ant-design/components/basic-views/list#resource) - Resources (https://refine.dev/docs/api-reference/core/components/refine-config/#resources) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
fascinating-indigo
fascinating-indigo2mo ago
@kapa.ai Not working as expected. First, I see a 'dashcamera' menu option which I don't want. I want only a single one - devices. Here is the app.tsx: <Route path="/devices" element={ <DevicessListPage> <Outlet /> </DevicessListPage> } > <Route index element={null} /> <Route path="show/:id" element={<DriverShowPage />} /> <Route path="create" element={ <DriverCreatePage> <Outlet /> </DriverCreatePage> } > </Route> </Route> And the resources: export const resources: IResourceItem[] = [ { name: "dashboard", list: "/", meta: { label: "Dashboard", icon: <DashboardOutlined />, } }, { name: "vehicles", list: "/vehicles", create: "/vehicles/create", edit: "/vehicles/edit/:id", show: "/vehicles/show/:id", meta: { canDelete: true, icon: <CarOutlined/> }, }, { name: "drivers", list: "/drivers", create: "/drivers/create", edit: "/drivers/edit/:id", show: "/drivers/show/:id", meta: { label: "Drivers", icon: <TeamOutlined />, }, }, { name: "gateways", list: "/devices", create: "/gateways/create", edit: "/gateways/edit/:id", show: "/gateways/show/:id", meta: { label: "Devices", icon: <TeamOutlined />, }, }, { name: "dashcameras", list: "/devices", // create: "/dashcameras/create", // edit: "/dashcameras/edit/:id", // show: "/dashcameras/show/:id", }, ];
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- If you want to have a single menu item for both gateways and dashcameras and display them on the same page, you can create a custom page and use the <List> component from Refine for each resource. First, define your resources as you have done:
export const resources: IResourceItem[] = [
// ... other resources
{{
name: "gateways",
list: "/devices",
create: "/gateways/create",
edit: "/gateways/edit/:id",
show: "/gateways/show/:id",
meta: {{
label: "Devices",
icon: <TeamOutlined />,
}},
}},
{{
name: "dashcameras",
// list: "/devices", // remove this line
// create: "/dashcameras/create",
// edit: "/dashcameras/edit/:id",
// show: "/dashcameras/show/:id",
}},
];
export const resources: IResourceItem[] = [
// ... other resources
{{
name: "gateways",
list: "/devices",
create: "/gateways/create",
edit: "/gateways/edit/:id",
show: "/gateways/show/:id",
meta: {{
label: "Devices",
icon: <TeamOutlined />,
}},
}},
{{
name: "dashcameras",
// list: "/devices", // remove this line
// create: "/dashcameras/create",
// edit: "/dashcameras/edit/:id",
// show: "/dashcameras/show/:id",
}},
];
Then, create a custom page that uses the <List> component for each resource:
import {{ List }} from "@refinedev/antd";

const DevicesPage: React.FC = () => {{
return (
<div>
<h1>Gateways</h1>
<List resource="gateways">
{{/* Rest of your page here */}}
</List>

<h1>Dash Cameras</h1>
<List resource="dashcameras">
{{/* Rest of your page here */}}
</List>
</div>
);
}};
import {{ List }} from "@refinedev/antd";

const DevicesPage: React.FC = () => {{
return (
<div>
<h1>Gateways</h1>
<List resource="gateways">
{{/* Rest of your page here */}}
</List>

<h1>Dash Cameras</h1>
<List resource="dashcameras">
{{/* Rest of your page here */}}
</List>
</div>
);
}};
Finally, update your routes in App.tsx to use the DevicesPage:
<Route path="/devices" element={<DevicesPage />} />
<Route path="/devices" element={<DevicesPage />} />
In this example, DevicesPage is your custom component where you can use Refine's hooks and components to fetch and display data from your API. The resource prop in the <List> component specifies which resource's data to fetch and display. For more information, you can refer to the following documentation: - List Component (https://refine.dev/docs/ui-integrations/ant-design/components/basic-views/list#resource) - Resources (https://refine.dev/docs/api-reference/core/components/refine-config/#resources) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
fascinating-indigo
fascinating-indigo2mo ago
@kapa.ai It's showing only the gateway data now.. Here is the list component for DevicessListPage: <List breadcrumb={false} headerButtons={() => { return ( <Space style={{ marginTop: screens.xs ? "1.6rem" : undefined, }} > <Form {...dashcameraSearchFormProps} layout="inline"> <Form.Item name="name" noStyle> <Input size="large" prefix={<SearchOutlined className="anticon tertiary" />} suffix={ <Spin size="small" spinning={dashcameraTableQueryResult.isFetching} /> } placeholder="Search by serial" onChange={dashcameraDebouncedOnChange} /> </Form.Item> </Form> </Space> ); }} contentProps={{ style: { marginTop: "28px", }, }} > {<DashcameraTableView tableProps={dashcameraTableProps} filters={dashcameraFilters} sorters={dashcameraSorters} />} {children} </List>
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- It seems like you're only using the <List> component for dashcameras in your DevicesListPage. If you want to display both gateways and dashcameras data, you need to use two <List> components, one for each resource. Here's how you can modify your DevicesListPage to include both:
import { List, Space, Form, Input, Spin } from "@refinedev/antd";
import { SearchOutlined } from "@ant-design/icons";

const DevicesListPage: React.FC = ({ children }) => {
// ... other code

return (
<div>
<h1>Gateways</h1>
<List
resource="gateways"
// ... other props
>
{/* Rest of your page here */}
{children}
</List>
<h1>Dash Cameras</h1>
<List
resource="dashcameras"
breadcrumb={false}
headerButtons={() => {
return (
<Space
style={{
marginTop: screens.xs ? "1.6rem" : undefined,
}}
>
<Form {...dashcameraSearchFormProps} layout="inline">
<Form.Item name="name" noStyle>
<Input
size="large"
prefix={<SearchOutlined className="anticon tertiary" />}
suffix={
<Spin
size="small"
spinning={dashcameraTableQueryResult.isFetching}
/>
}
placeholder="Search by serial"
onChange={dashcameraDebouncedOnChange}
/>
</Form.Item>
</Form>
</Space>
);
}}
contentProps={{
style: {
marginTop: "28px",
},
}}
>
<DashcameraTableView
tableProps={dashcameraTableProps}
filters={dashcameraFilters}
sorters={dashcameraSorters}
/>
{children}
</List>
</div>
);
};
import { List, Space, Form, Input, Spin } from "@refinedev/antd";
import { SearchOutlined } from "@ant-design/icons";

const DevicesListPage: React.FC = ({ children }) => {
// ... other code

return (
<div>
<h1>Gateways</h1>
<List
resource="gateways"
// ... other props
>
{/* Rest of your page here */}
{children}
</List>
<h1>Dash Cameras</h1>
<List
resource="dashcameras"
breadcrumb={false}
headerButtons={() => {
return (
<Space
style={{
marginTop: screens.xs ? "1.6rem" : undefined,
}}
>
<Form {...dashcameraSearchFormProps} layout="inline">
<Form.Item name="name" noStyle>
<Input
size="large"
prefix={<SearchOutlined className="anticon tertiary" />}
suffix={
<Spin
size="small"
spinning={dashcameraTableQueryResult.isFetching}
/>
}
placeholder="Search by serial"
onChange={dashcameraDebouncedOnChange}
/>
</Form.Item>
</Form>
</Space>
);
}}
contentProps={{
style: {
marginTop: "28px",
},
}}
>
<DashcameraTableView
tableProps={dashcameraTableProps}
filters={dashcameraFilters}
sorters={dashcameraSorters}
/>
{children}
</List>
</div>
);
};
In this example, DevicesListPage is your custom component where you can use Refine's hooks and components to fetch and display data from your API. The resource prop in the <List> component specifies which resource's data to fetch and display. For more information, you can refer to the following documentation: - List Component (https://refine.dev/docs/ui-integrations/ant-design/components/basic-views/list#resource) - Resources (https://refine.dev/docs/api-reference/core/components/refine-config/#resources) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
fascinating-indigo
fascinating-indigo2mo ago
@kapa.ai This is what I did but it gives me error that TypeError: Cannot read properties of undefined (reading 'nodes') Seems like there is an issue with how I define the resource: export const resources: IResourceItem[] = [ { name: "dashboard", list: "/", meta: { label: "Dashboard", icon: <DashboardOutlined />, } }, { name: "vehicles", list: "/vehicles", create: "/vehicles/create", edit: "/vehicles/edit/:id", show: "/vehicles/show/:id", meta: { canDelete: true, icon: <CarOutlined/> }, }, { name: "drivers", list: "/drivers", create: "/drivers/create", edit: "/drivers/edit/:id", show: "/drivers/show/:id", meta: { label: "Drivers", icon: <TeamOutlined />, }, }, { name: "gateways", list: "/devices", create: "/gateways/create", edit: "/gateways/edit/:id", show: "/gateways/show/:id", meta: { label: "Devices", icon: <TeamOutlined />, }, }, { name: "dashcameras" // list: "/devices", // create: "/dashcameras/create", // edit: "/dashcameras/edit/:id", // show: "/dashcameras/show/:id", }, ]; Or set app.tsx: <Refine dataProvider={dataProvider} // liveProvider={liveProvider} notificationProvider={useNotificationProvider} routerProvider={routerBindings} authProvider={authProvider} resources={resources} options={{ liveMode: "auto", syncWithLocation: true, warnWhenUnsavedChanges: true, useNewQueryKeys: true, }} > ... <Route path="/devices" element={ <DevicessListPage> <Outlet /> </DevicessListPage> } > <Route index element={null} /> <Route path="show/:id" element={<DriverShowPage />} /> <Route path="create" element={ <DriverCreatePage> <Outlet /> </DriverCreatePage> } > </Route> </Route>
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The error message "TypeError: Cannot read properties of undefined (reading 'nodes')" suggests that the data returned from your API might not be in the expected format. Refine expects the data from the getList method of the data provider to be in the format { data: YourDataType[], total: number }. If your API returns data in a different format, you might need to adjust your data provider to transform the data into the expected format. For example, if your API returns data in the format { nodes: YourDataType[], count: number }, you would need to adjust your data provider like this:
const dataProvider = {
// ... other methods
getList: async (resource, params) => {
const response = await fetch(`/api/${resource}`, {
method: "GET",
// ... other necessary options
});
const data = await response.json();

return {
data: data.nodes,
total: data.count,
};
},
// ... other methods
};
const dataProvider = {
// ... other methods
getList: async (resource, params) => {
const response = await fetch(`/api/${resource}`, {
method: "GET",
// ... other necessary options
});
const data = await response.json();

return {
data: data.nodes,
total: data.count,
};
},
// ... other methods
};
This is just an example and you might need to adjust it based on how your API works. For more information, you can refer to the following documentation: - Data Provider (https://refine.dev/docs/api-reference/providers/data-provider) - Get List (https://refine.dev/docs/api-reference/providers/data-provider#getlist) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below
fascinating-indigo
fascinating-indigo2mo ago
@kapa.ai But how would I then modify the data-provider to fit the dashcamera/gateway issue we had under devices?
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To modify the data provider to fit the dashcamera and gateway issue under devices, you would need to customize the getList method in your data provider for each resource. Here's an example of how you can do it:
const dataProvider = {
// ... other methods
getList: async (resource, params) => {
let response;
let data;

if (resource === "gateways" || resource === "dashcameras") {
response = await fetch(`/api/devices`, {
method: "GET",
// ... other necessary options
});
data = await response.json();

// filter the data based on the resource
const filteredData = data.nodes.filter(item => item.type === resource);

return {
data: filteredData,
total: data.count,
};
} else {
response = await fetch(`/api/${resource}`, {
method: "GET",
// ... other necessary options
});
data = await response.json();

return {
data: data.nodes,
total: data.count,
};
}
},
// ... other methods
};
const dataProvider = {
// ... other methods
getList: async (resource, params) => {
let response;
let data;

if (resource === "gateways" || resource === "dashcameras") {
response = await fetch(`/api/devices`, {
method: "GET",
// ... other necessary options
});
data = await response.json();

// filter the data based on the resource
const filteredData = data.nodes.filter(item => item.type === resource);

return {
data: filteredData,
total: data.count,
};
} else {
response = await fetch(`/api/${resource}`, {
method: "GET",
// ... other necessary options
});
data = await response.json();

return {
data: data.nodes,
total: data.count,
};
}
},
// ... other methods
};
In this example, if the resource is either gateways or dashcameras, we fetch the data from /api/devices and then filter the data based on the resource type. If the resource is not gateways or dashcameras, we fetch the data as usual. Please note that this is just an example and you might need to adjust it based on how your API works. For more information, you can refer to the following documentation: - Data Provider (https://refine.dev/docs/api-reference/providers/data-provider) - Get List (https://refine.dev/docs/api-reference/providers/data-provider#getlist) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@240371095444652032> kapa.ai is still learning and improving, please let us know how it did by reacting below