How do I upload files with antd and supabase?

import { getValueFromEvent, useModalForm, useSelect } from "@refinedev/antd"; import { useApiUrl, useGo } from "@refinedev/core"; import { Form, Modal, Input, Grid, Switch, Divider, Row, Col, Select, DatePicker, Upload, } from "antd"; import dayjs from "dayjs"; const JobCreate = () => { const go = useGo(); const apiUrl = useApiUrl(); const goToListPage = () => { go({ to: { resource: "jobs", action: "list" }, options: { keepQuery: true, }, type: "replace", }); }; const { formProps: formProps, modalProps: modalProps } = useModalForm({ resource: "jobs", action: "create", onMutationSuccess: goToListPage, defaultVisible: true, redirect: false, }); const { selectProps: userSelectProps } = useSelect({ resource: "users", optionLabel: (item) => ${item.first_name} ${item.last_name}, optionValue: "id", }); return ( <> <Modal {...modalProps} mask={true} width={560} destroyOnClose onCancel={goToListPage} title={"Create Job"} > <Divider /> <Form {...formProps} layout="vertical"> <Form.Item label="Address" name="address" rules={[{ required: true }]} > <Input /> </Form.Item> <Form.Item label="Staff" name="user_ids" required> <Select {...userSelectProps} mode="multiple" /> </Form.Item> </Form> <Divider /> </Modal> </> ); }; export default JobCreate; why is my website crashing when using useApiUrl?
11 Replies
kapa.ai
kapa.ai3w ago
Hi @daw. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To upload files with Ant Design (antd) and Supabase in a Refine application, you need to ensure that you are correctly using the useApiUrl hook to get the API URL for your file upload endpoint. Below is an example of how you can integrate file upload functionality into your form using the Upload.Dragger component from antd and the useApiUrl hook from Refine. Here is a complete example based on the provided code and the knowledge sources:
import { getValueFromEvent, useModalForm, useSelect } from "@refinedev/antd";
import { useApiUrl, useGo } from "@refinedev/core";
import {
Form,
Modal,
Input,
Divider,
Select,
Upload,
} from "antd";

const JobCreate = () => {
const go = useGo();
const apiUrl = useApiUrl();

const goToListPage = () => {
go({
to: { resource: "jobs", action: "list" },
options: {
keepQuery: true,
},
type: "replace",
});
};

const { formProps, modalProps } = useModalForm({
resource: "jobs",
action: "create",
onMutationSuccess: goToListPage,
defaultVisible: true,
redirect: false,
});

const { selectProps: userSelectProps } = useSelect({
resource: "users",
optionLabel: (item) => `${item.first_name} ${item.last_name}`,
optionValue: "id",
});
return (
<>
<Modal
{...modalProps}
mask={true}
width={560}
destroyOnClose
onCancel={goToListPage}
title={"Create Job"}
>
<Divider />
<Form {...formProps} layout="vertical">
<Form.Item
label="Address"
name="address"
rules={[{ required: true }]}
>
<Input />
</Form.Item>
<Form.Item label="Staff" name="user_ids" required>
<Select {...userSelectProps} mode="multiple" />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
<Divider />
</Modal>
</>
);
};

export default JobCreate;
import { getValueFromEvent, useModalForm, useSelect } from "@refinedev/antd";
import { useApiUrl, useGo } from "@refinedev/core";
import {
Form,
Modal,
Input,
Divider,
Select,
Upload,
} from "antd";

const JobCreate = () => {
const go = useGo();
const apiUrl = useApiUrl();

const goToListPage = () => {
go({
to: { resource: "jobs", action: "list" },
options: {
keepQuery: true,
},
type: "replace",
});
};

const { formProps, modalProps } = useModalForm({
resource: "jobs",
action: "create",
onMutationSuccess: goToListPage,
defaultVisible: true,
redirect: false,
});

const { selectProps: userSelectProps } = useSelect({
resource: "users",
optionLabel: (item) => `${item.first_name} ${item.last_name}`,
optionValue: "id",
});
return (
<>
<Modal
{...modalProps}
mask={true}
width={560}
destroyOnClose
onCancel={goToListPage}
title={"Create Job"}
>
<Divider />
<Form {...formProps} layout="vertical">
<Form.Item
label="Address"
name="address"
rules={[{ required: true }]}
>
<Input />
</Form.Item>
<Form.Item label="Staff" name="user_ids" required>
<Select {...userSelectProps} mode="multiple" />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
<Divider />
</Modal>
</>
);
};

export default JobCreate;
Explanation: 1. useApiUrl Hook: The useApiUrl hook is used to get the base API URL. This URL is then used in the Upload.Dragger component's action prop to specify the endpoint for file uploads. 2. Upload.Dragger Component: This component from antd is used to create a drag-and-drop area for file uploads. The action prop is set to ${apiUrl}/media/upload, which points to the file upload endpoint. 3. getValueFromEvent: This function is used to handle the file list from the upload component. Troubleshooting: If your website is crashing when using useApiUrl, ensure that: - The useApiUrl hook is correctly imported from `@ ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@243918634374660098> kapa.ai is still learning and improving, please let us know how it did by reacting below
xenophobic-harlequin
xenophobic-harlequin3w ago
@kapa.ai is the useApiUrl hook supposed to work out the box?
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Yes, the useApiUrl hook is designed to work out of the box in a Refine application. It retrieves the API URL from the dataProvider that you have configured in your Refine setup. Here are a few things to check to ensure it works correctly: 1. Ensure Proper Import: Make sure you have imported the useApiUrl hook correctly from @refinedev/core.
import { useApiUrl } from "@refinedev/core";

import { useApiUrl } from "@refinedev/core";

2. Data Provider Configuration: Ensure that you have configured the dataProvider correctly in your Refine setup. The dataProvider should include the getApiUrl method which returns the base API URL.
import { Refine } from "@refinedev/core";
import { dataProvider } from "./dataProvider"; // Adjust the import according to your file structure

const App = () => {
return (
<Refine
dataProvider={{
default: dataProvider("<https://api.example.com">),
other: dataProvider("<https://other-api.example.com">),
}}
>
{/* ... */}
</Refine>
);
};

import { Refine } from "@refinedev/core";
import { dataProvider } from "./dataProvider"; // Adjust the import according to your file structure

const App = () => {
return (
<Refine
dataProvider={{
default: dataProvider("<https://api.example.com">),
other: dataProvider("<https://other-api.example.com">),
}}
>
{/* ... */}
</Refine>
);
};

3. Usage in Component: Use the useApiUrl hook in your component to get the API URL.
const apiUrl = useApiUrl();

const apiUrl = useApiUrl();

4. Optional Data Provider Name: If you have multiple data providers, you can pass the dataProviderName parameter to the useApiUrl hook to get the URL of a specific data provider.
const apiUrl = useApiUrl("other");

const apiUrl = useApiUrl("other");

Example Usage Here is an example of how to use the useApiUrl hook in a component:
import { useApiUrl } from "@refinedev/core";
import { Upload } from "antd";

const MyComponent = () => {
const apiUrl = useApiUrl();
return (
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
);
};
import { useApiUrl } from "@refinedev/core";
import { Upload } from "antd";

const MyComponent = () => {
const apiUrl = useApiUrl();
return (
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
);
};
Documentation Links - useApiUrl Hook Documentation (https://refine.dev/docs/data/hooks/use-api-url/) - Data Provider Configuration (https://refine.dev/docs/data/data-provider/#getapiurl-) If you have followed these steps and the useApiUrl hook is still not working, please check the console for any errors and ensure that the dataProvider is correctly set up and returning the expected API URL. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@243918634374660098> kapa.ai is still learning and improving, please let us know how it did by reacting below
xenophobic-harlequin
xenophobic-harlequin3w ago
Uncaught Error: Not implemented on refine-supabase data provider. @kapa.ai this is the message in the console
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The error message "Not implemented on refine-supabase data provider" indicates that the getApiUrl method is not implemented in the refine-supabase data provider. This is confirmed by the knowledge source:
Source: Blog > Setting Up the Client App > Exploring the App​ > <Refine />'s dataProvider Prop​ Part 5 (https://refine.dev/blog/refine-pixels-2/#refine-s-dataprovider-prop)
getApiUrl: () => {
throw Error("Not implemented on refine-supabase data provider.");
},
getApiUrl: () => {
throw Error("Not implemented on refine-supabase data provider.");
},
Solution Since the getApiUrl method is not implemented in the refine-supabase data provider, you will need to manually specify the API URL in your component instead of relying on the useApiUrl hook. Here is how you can modify your component to manually set the API URL:
import { getValueFromEvent, useModalForm, useSelect } from "@refinedev/antd";
import { useGo } from "@refinedev/core";
import {
Form,
Modal,
Input,
Divider,
Select,
Upload,
} from "antd";

const JobCreate = () => {
const go = useGo();
const apiUrl = "<https://your-supabase-api-url";> // Manually set your Supabase API URL

const goToListPage = () => {
go({
to: { resource: "jobs", action: "list" },
options: {
keepQuery: true,
},
type: "replace",
});
};

const { formProps, modalProps } = useModalForm({
resource: "jobs",
action: "create",
onMutationSuccess: goToListPage,
defaultVisible: true,
redirect: false,
});

const { selectProps: userSelectProps } = useSelect({
resource: "users",
optionLabel: (item) => `${item.first_name} ${item.last_name}`,
optionValue: "id",
});
return (
<>
<Modal
{...modalProps}
mask={true}
width={560}
destroyOnClose
onCancel={goToListPage}
title={"Create Job"}
>
<Divider />
<Form {...formProps} layout="vertical">
<Form.Item
label="Address"
name="address"
rules={[{ required: true }]}
>
<Input />
</Form.Item>
<Form.Item label="Staff" name="user_ids" required>
<Select {...userSelectProps} mode="multiple" />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
<Divider />
</Modal>
</>
);
};

export default JobCreate;
import { getValueFromEvent, useModalForm, useSelect } from "@refinedev/antd";
import { useGo } from "@refinedev/core";
import {
Form,
Modal,
Input,
Divider,
Select,
Upload,
} from "antd";

const JobCreate = () => {
const go = useGo();
const apiUrl = "<https://your-supabase-api-url";> // Manually set your Supabase API URL

const goToListPage = () => {
go({
to: { resource: "jobs", action: "list" },
options: {
keepQuery: true,
},
type: "replace",
});
};

const { formProps, modalProps } = useModalForm({
resource: "jobs",
action: "create",
onMutationSuccess: goToListPage,
defaultVisible: true,
redirect: false,
});

const { selectProps: userSelectProps } = useSelect({
resource: "users",
optionLabel: (item) => `${item.first_name} ${item.last_name}`,
optionValue: "id",
});
return (
<>
<Modal
{...modalProps}
mask={true}
width={560}
destroyOnClose
onCancel={goToListPage}
title={"Create Job"}
>
<Divider />
<Form {...formProps} layout="vertical">
<Form.Item
label="Address"
name="address"
rules={[{ required: true }]}
>
<Input />
</Form.Item>
<Form.Item label="Staff" name="user_ids" required>
<Select {...userSelectProps} mode="multiple" />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
<Divider />
</Modal>
</>
);
};

export default JobCreate;
Explanation - Manual API URL: The apiUrl is manually set to your Supabase API URL. - Upload Component: The action prop of the Upload.Dragger component ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@243918634374660098> kapa.ai is still learning and improving, please let us know how it did by reacting below
xenophobic-harlequin
xenophobic-harlequin3w ago
@kapa.ai what type should my images column be in my table?
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- For storing image URLs in your table, you should use a column type that can handle arrays of objects, where each object contains a URL string. Based on the knowledge sources, the image column should be an array of objects with a url property. Example Table Schema If you are using Supabase, you can define your table schema to include an images column that stores an array of JSON objects. Here is an example SQL script to create such a table:
create table jobs (
id uuid primary key default uuid_generate_v4(),
address text not null,
user_ids uuid[] not null,
images jsonb[] -- This column will store an array of JSON objects
);
create table jobs (
id uuid primary key default uuid_generate_v4(),
address text not null,
user_ids uuid[] not null,
images jsonb[] -- This column will store an array of JSON objects
);
Example Interface In your TypeScript code, you can define an interface for the images column as follows:
interface IImage {
url: string;
}

interface IJob {
id: string;
address: string;
user_ids: string[];
images: IImage[]; // Array of image objects
}
interface IImage {
url: string;
}

interface IJob {
id: string;
address: string;
user_ids: string[];
images: IImage[]; // Array of image objects
}
Example Usage in Refine When using Refine with Ant Design, you can handle the images column in your form as shown below:
import { getValueFromEvent, useModalForm, useSelect } from "@refinedev/antd";
import { useGo } from "@refinedev/core";
import { Form, Modal, Input, Divider, Select, Upload } from "antd";

const JobCreate = () => {
const go = useGo();
const apiUrl = "<https://your-supabase-api-url";> // Manually set your Supabase API URL

const goToListPage = () => {
go({
to: { resource: "jobs", action: "list" },
options: {
keepQuery: true,
},
type: "replace",
});
};

const { formProps, modalProps } = useModalForm({
resource: "jobs",
action: "create",
onMutationSuccess: goToListPage,
defaultVisible: true,
redirect: false,
});
const { selectProps: userSelectProps } = useSelect({
resource: "users",
optionLabel: (item) => `${item.first_name} ${item.last_name}`,
optionValue: "id",
});

return (
<>
<Modal
{...modalProps}
mask={true}
width={560}
destroyOnClose
onCancel={goToListPage}
title={"Create Job"}
>
<Divider />
<Form {...formProps} layout="vertical">
<Form.Item
label="Address"
name="address"
rules={[{ required: true }]}
>
<Input />
</Form.Item>
<Form.Item label="Staff" name="user_ids" required>
<Select {...userSelectProps} mode="multiple" />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="images"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
<Divider />
</Modal>
</>
);
};

export default JobCreate;
import { getValueFromEvent, useModalForm, useSelect } from "@refinedev/antd";
import { useGo } from "@refinedev/core";
import { Form, Modal, Input, Divider, Select, Upload } from "antd";

const JobCreate = () => {
const go = useGo();
const apiUrl = "<https://your-supabase-api-url";> // Manually set your Supabase API URL

const goToListPage = () => {
go({
to: { resource: "jobs", action: "list" },
options: {
keepQuery: true,
},
type: "replace",
});
};

const { formProps, modalProps } = useModalForm({
resource: "jobs",
action: "create",
onMutationSuccess: goToListPage,
defaultVisible: true,
redirect: false,
});
const { selectProps: userSelectProps } = useSelect({
resource: "users",
optionLabel: (item) => `${item.first_name} ${item.last_name}`,
optionValue: "id",
});

return (
<>
<Modal
{...modalProps}
mask={true}
width={560}
destroyOnClose
onCancel={goToListPage}
title={"Create Job"}
>
<Divider />
<Form {...formProps} layout="vertical">
<Form.Item
label="Address"
name="address"
rules={[{ required: true }]}
>
<Input />
</Form.Item>
<Form.Item label="Staff" name="user_ids" required>
<Select {...userSelectProps} mode="multiple" />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="images"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={5}
multiple
>
<p className="ant-upload-text">Drag & drop a file in this area</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
<Divider />
</Modal>
</>
);
};

export default JobCreate;
Summary - ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@243918634374660098> kapa.ai is still learning and improving, please let us know how it did by reacting below