Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Resource routes using path param other than id not being substituted in URL #6264

Closed
KGMaxey opened this issue Aug 16, 2024 · 1 comment
Labels
bug Something isn't working

Comments

@KGMaxey
Copy link

KGMaxey commented Aug 16, 2024

Describe the bug

We would like to route based on a unique slug using react router and the refine resource definitions rather than the ids to have more readable URLs. When we try and use a resource path such as show: "/publishers/show/:handle" it doesn't substitute the handle field but rather puts :handle directly in the URL.

Do Refine resources only accept the :id path param?

Example App.tsx after bootstrap

import { Authenticated, GitHubBanner, Refine } from "@refinedev/core";
import { DevtoolsPanel, DevtoolsProvider } from "@refinedev/devtools";
import { RefineKbar, RefineKbarProvider } from "@refinedev/kbar";

import {
  AuthPage,
  ErrorComponent,
  ThemedLayoutV2,
  ThemedSiderV2,
  useNotificationProvider,
} from "@refinedev/antd";
import "@refinedev/antd/dist/reset.css";

import routerBindings, {
  CatchAllNavigate,
  DocumentTitleHandler,
  NavigateToResource,
  UnsavedChangesNotifier,
} from "@refinedev/react-router-v6";
import { dataProvider, liveProvider } from "@refinedev/supabase";
import { App as AntdApp } from "antd";
import { BrowserRouter, Outlet, Route, Routes } from "react-router-dom";
import authProvider from "./authProvider";
import { Header } from "./components/header";
import { ColorModeContextProvider } from "./contexts/color-mode";
import { supabaseClient } from "./utility";
import { PublisherCreate, PublishersEdit, PublisherList, PublisherShow } from "./pages/publishers";

function App() {
  return (
    (<BrowserRouter>
      <GitHubBanner />
      <RefineKbarProvider>
        <ColorModeContextProvider>
          <AntdApp>
            <DevtoolsProvider>
              <Refine
                dataProvider={dataProvider(supabaseClient)}
                liveProvider={liveProvider(supabaseClient)}
                authProvider={authProvider}
                routerProvider={routerBindings}
                notificationProvider={useNotificationProvider}
                resources={[{
                  name: "publisher",
                  list: "/publishers",
                  show: "/publishers/show/:handle",
                  meta: {
                    label: 'Publishers',
                  }
                }]}
                options={{
                  syncWithLocation: true,
                  warnWhenUnsavedChanges: true,
                  useNewQueryKeys: true,
                  projectId: "******",
                }}
              >
                <Routes>
                  <Route
                    element={
                      <Authenticated
                        key="authenticated-inner"
                        fallback={<CatchAllNavigate to="/login" />}
                      >
                        <ThemedLayoutV2
                          Header={Header}
                          Sider={(props) => <ThemedSiderV2 {...props} fixed />}
                        >
                          <Outlet />
                        </ThemedLayoutV2>
                      </Authenticated>
                    }
                  >
                    <Route
                      index
                      element={<NavigateToResource resource="publisher" />}
                    />
                    <Route path="/publishers">
                      <Route index element={<PublisherList />} />
                      <Route path="show/:handle" element={<PublisherShow />} />
                    </Route>
                    <Route path="*" element={<ErrorComponent />} />
                  </Route>
                  <Route
                    element={
                      <Authenticated
                        key="authenticated-outer"
                        fallback={<Outlet />}
                      >
                        <NavigateToResource />
                      </Authenticated>
                    }
                  >
                    <Route
                      path="/login"
                      element={
                        <AuthPage
                          type="login"
                          formProps={{
                            initialValues: {
                              email: "[email protected]",
                              password: "refine-supabase",
                            },
                          }}
                        />
                      }
                    />
                    <Route
                      path="/register"
                      element={<AuthPage type="register" />}
                    />
                    <Route
                      path="/forgot-password"
                      element={<AuthPage type="forgotPassword" />}
                    />
                  </Route>
                </Routes>

                <RefineKbar />
                <UnsavedChangesNotifier />
                <DocumentTitleHandler />
              </Refine>
              <DevtoolsPanel />
            </DevtoolsProvider>
          </AntdApp>
        </ColorModeContextProvider>
      </RefineKbarProvider>
    </BrowserRouter>)
  );
}

export default App;

Example pages/publishers/list.tsx

import { useTable, List, EditButton, ShowButton } from "@refinedev/antd";
import { Table, Space } from "antd";
import { Publisher } from "../../types/publisher";

export const PublisherList = () => {
    const { tableProps } = useTable<Publisher>({ });

    return (
        <List>
            <Table {...tableProps} rowKey="handle">
                <Table.Column
                  dataIndex="id"
                  title="Id"
                />
                <Table.Column
                  dataIndex="publisher_name"
                  title="Publisher Name"
                />
                <Table.Column
                  dataIndex="handle"
                  title="Handle"
                />
                <Table.Column
                    title="Actions"
                    dataIndex="actions"
                    render={(_, record: Publisher) => (
                        <Space>
                            <EditButton
                                hideText
                                size="small"
                                recordItemId={record.handle}
                            />
                            <ShowButton
                                hideText
                                size="small"
                                recordItemId={record.handle}
                            />
                        </Space>
                    )}
                />
            </Table>
        </List>
    );
};

Example pages/publishers/show.tsx

import { Show, TextField } from "@refinedev/antd";
import { useShow } from "@refinedev/core";
import { Typography } from "antd";
import { Publisher } from "../../types/publisher";

const { Title } = Typography;

export const PublisherShow = () => {
  const { queryResult } = useShow<Publisher>({ });
  const { data, isLoading } = queryResult;

  const record = data?.data;

  return (
    <Show isLoading={isLoading}>
      <Title level={5}>{"ID"}</Title>
      <TextField value={record?.id} />
      <Title level={5}>{"Publisher Name"}</Title>
      <TextField value={record?.publisher_name} />
      <Title level={5}>{"Handle"}</Title>
      <TextField value={record?.handle} />
    </Show>
  );
};

Steps To Reproduce

  1. Use Refine CLI to bootstrap project with Vite, React Router, Supabase, and AntDesign
  2. Copy example App.tsx and publisher pages from bug description
  3. Replace projectId with your projectId
  4. Configure supabase client (an unlinked local project will work)
  5. Add publisher table with columns: id, publisher_name, handle into supabase
  6. Add row with publisher_name: "Test Publisher" and handle: "test-publisher"
  7. Start refine app and open in browser
  8. Find Test Publisher in table and click the view icon
  9. Note the url includes the raw string :handle

Expected behavior

Refine correctly substitutes the :handle param with the handle field from the entity in the URL.

Packages

  • @refinedev/antd
  • @refinedev/core
  • @refinedev/react-router-v6

Additional Context

No response

@KGMaxey KGMaxey added the bug Something isn't working label Aug 16, 2024
@KGMaxey KGMaxey changed the title [BUG] Resource routes using path param other than id [BUG] Resource routes using path param other than id not being substituted in URL Aug 16, 2024
@KGMaxey
Copy link
Author

KGMaxey commented Aug 16, 2024

Found it. It looks like this is because ShowButton actually functions under the assumption that you're using the id param and only substitutes that. Quick fix for anyone interested would be to swizzle out that component or just use the onClick param, that seems to override the behavior. So something like:

                            <ShowButton
                                hideText
                                size="small"
                                onClick={() => go({ to: `show/${record.handle}` })}
                            />

@KGMaxey KGMaxey closed this as completed Aug 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant