Added import external filament in filament create form
This commit is contained in:
@@ -224,7 +224,9 @@
|
|||||||
"show_title": "[Filament #{{id}}] {{name}}"
|
"show_title": "[Filament #{{id}}] {{name}}"
|
||||||
},
|
},
|
||||||
"form": {
|
"form": {
|
||||||
"filament_updated": "This filament has been updated by someone/something else since you opened this page. Saving will overwrite those changes!"
|
"filament_updated": "This filament has been updated by someone/something else since you opened this page. Saving will overwrite those changes!",
|
||||||
|
"import_external": "Import from External",
|
||||||
|
"import_external_description": "Select a filament in the list to automatically populate its details in the filament creation form. This will overwrite any data you have entered in the form.<br><br>A Manufacturer object will be created automatically when you click Ok, if necessary."
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"add_spool": "Add Spool"
|
"add_spool": "Add Spool"
|
||||||
|
|||||||
66
client/src/components/filamentImportModal.tsx
Normal file
66
client/src/components/filamentImportModal.tsx
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { Form, InputNumber, Modal, Select } from "antd";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { ExternalFilament, useGetExternalDBFilaments } from "../utils/queryExternalDB";
|
||||||
|
import { formatFilamentLabel } from "../pages/spools/functions";
|
||||||
|
import { searchMatches } from "../utils/filtering";
|
||||||
|
import { useTranslate } from "@refinedev/core";
|
||||||
|
import { Trans } from "react-i18next";
|
||||||
|
|
||||||
|
export function FilamentImportModal(props: {
|
||||||
|
isOpen: boolean;
|
||||||
|
onImport: (filament: ExternalFilament) => void;
|
||||||
|
onClose: () => void;
|
||||||
|
}) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const t = useTranslate();
|
||||||
|
|
||||||
|
const externalFilaments = useGetExternalDBFilaments();
|
||||||
|
const filamentOptions =
|
||||||
|
externalFilaments.data?.map((item) => {
|
||||||
|
return {
|
||||||
|
label: formatFilamentLabel(item.name, item.diameter, item.manufacturer, item.material, item.weight),
|
||||||
|
value: item.id,
|
||||||
|
item: item,
|
||||||
|
};
|
||||||
|
}) ?? [];
|
||||||
|
filamentOptions.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: "base" }));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={t("filament.form.import_external")}
|
||||||
|
open={props.isOpen}
|
||||||
|
onOk={() => form.submit()}
|
||||||
|
onCancel={() => props.onClose()}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
layout="vertical"
|
||||||
|
form={form}
|
||||||
|
onFinish={(values) => {
|
||||||
|
const filament = filamentOptions.find((item) => item.value === values.filament)?.item;
|
||||||
|
if (!filament) {
|
||||||
|
throw new Error("Filament not found");
|
||||||
|
}
|
||||||
|
props.onImport(filament);
|
||||||
|
props.onClose();
|
||||||
|
form.resetFields();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<Trans
|
||||||
|
i18nKey={"filament.form.import_external_description"}
|
||||||
|
components={{
|
||||||
|
br: <br />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<Form.Item name="filament" rules={[{ required: true }]}>
|
||||||
|
<Select
|
||||||
|
options={filamentOptions}
|
||||||
|
showSearch
|
||||||
|
filterOption={(input, option) => typeof option?.label === "string" && searchMatches(input, option?.label)}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { HttpError, IResourceComponentsProps, useTranslate } from "@refinedev/core";
|
import { HttpError, IResourceComponentsProps, useInvalidate, useTranslate } from "@refinedev/core";
|
||||||
import { Create, useForm, useSelect } from "@refinedev/antd";
|
import { Create, useForm, useSelect } from "@refinedev/antd";
|
||||||
import { Form, Input, Select, InputNumber, ColorPicker, Button, Typography } from "antd";
|
import { Form, Input, Select, InputNumber, ColorPicker, Button, Typography, Modal } from "antd";
|
||||||
import TextArea from "antd/es/input/TextArea";
|
import TextArea from "antd/es/input/TextArea";
|
||||||
import { numberFormatter, numberParser } from "../../utils/parsing";
|
import { numberFormatter, numberParser } from "../../utils/parsing";
|
||||||
import { IVendor } from "../vendors/model";
|
import { IVendor } from "../vendors/model";
|
||||||
@@ -11,6 +11,11 @@ import { ExtraFieldFormItem, StringifiedExtras } from "../../components/extraFie
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import { getCurrencySymbol, useCurrency } from "../../utils/settings";
|
import { getCurrencySymbol, useCurrency } from "../../utils/settings";
|
||||||
|
import { searchMatches } from "../../utils/filtering";
|
||||||
|
import { ExternalFilament, useGetExternalDBFilaments } from "../../utils/queryExternalDB";
|
||||||
|
import { formatFilamentLabel } from "../spools/functions";
|
||||||
|
import { FilamentImportModal } from "../../components/filamentImportModal";
|
||||||
|
import { getOrCreateVendorFromExternal } from "../vendors/functions";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
@@ -22,6 +27,8 @@ export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrClonePr
|
|||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const extraFields = useGetFields(EntityType.filament);
|
const extraFields = useGetFields(EntityType.filament);
|
||||||
const currency = useCurrency();
|
const currency = useCurrency();
|
||||||
|
const [isImportExtOpen, setIsImportExtOpen] = React.useState(false);
|
||||||
|
const invalidate = useInvalidate();
|
||||||
|
|
||||||
const { form, formProps, formLoading, onFinish, redirect } = useForm<
|
const { form, formProps, formLoading, onFinish, redirect } = useForm<
|
||||||
IFilament,
|
IFilament,
|
||||||
@@ -47,11 +54,32 @@ export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrClonePr
|
|||||||
redirect(redirectTo, (values as IFilament).id);
|
redirect(redirectTo, (values as IFilament).id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { selectProps } = useSelect<IVendor>({
|
const { selectProps: vendorSelect } = useSelect<IVendor>({
|
||||||
resource: "vendor",
|
resource: "vendor",
|
||||||
optionLabel: "name",
|
optionLabel: "name",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const importFilament = async (filament: ExternalFilament) => {
|
||||||
|
const vendor = await getOrCreateVendorFromExternal(filament.manufacturer);
|
||||||
|
await invalidate({
|
||||||
|
resource: "vendor",
|
||||||
|
invalidates: ["list", "detail"],
|
||||||
|
});
|
||||||
|
|
||||||
|
form.setFieldsValue({
|
||||||
|
name: filament.name,
|
||||||
|
vendor_id: vendor.id,
|
||||||
|
material: filament.material,
|
||||||
|
density: filament.density,
|
||||||
|
diameter: filament.diameter,
|
||||||
|
weight: filament.weight,
|
||||||
|
spool_weight: filament.spool_weight || undefined,
|
||||||
|
color_hex: filament.color_hex,
|
||||||
|
settings_extruder_temp: filament.extruder_temp || undefined,
|
||||||
|
settings_bed_temp: filament.bed_temp || undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Use useEffect to update the form's initialValues when the extra fields are loaded
|
// Use useEffect to update the form's initialValues when the extra fields are loaded
|
||||||
// This is necessary because the form is rendered before the extra fields are loaded
|
// This is necessary because the form is rendered before the extra fields are loaded
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -67,6 +95,13 @@ export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrClonePr
|
|||||||
<Create
|
<Create
|
||||||
title={props.mode === "create" ? t("filament.titles.create") : t("filament.titles.clone")}
|
title={props.mode === "create" ? t("filament.titles.create") : t("filament.titles.clone")}
|
||||||
isLoading={formLoading}
|
isLoading={formLoading}
|
||||||
|
headerButtons={() => (
|
||||||
|
<>
|
||||||
|
<Button type="primary" onClick={() => setIsImportExtOpen(true)}>
|
||||||
|
{t("filament.form.import_external")}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
footerButtons={() => (
|
footerButtons={() => (
|
||||||
<>
|
<>
|
||||||
<Button type="primary" onClick={() => handleSubmit("list")}>
|
<Button type="primary" onClick={() => handleSubmit("list")}>
|
||||||
@@ -78,6 +113,14 @@ export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrClonePr
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
<FilamentImportModal
|
||||||
|
isOpen={isImportExtOpen}
|
||||||
|
onImport={(value) => {
|
||||||
|
setIsImportExtOpen(false);
|
||||||
|
importFilament(value);
|
||||||
|
}}
|
||||||
|
onClose={() => setIsImportExtOpen(false)}
|
||||||
|
/>
|
||||||
<Form {...formProps} layout="vertical">
|
<Form {...formProps} layout="vertical">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("filament.fields.name")}
|
label={t("filament.fields.name")}
|
||||||
@@ -101,7 +144,7 @@ export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrClonePr
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
{...selectProps}
|
{...vendorSelect}
|
||||||
allowClear
|
allowClear
|
||||||
filterSort={(a, b) => {
|
filterSort={(a, b) => {
|
||||||
return a?.label && b?.label
|
return a?.label && b?.label
|
||||||
|
|||||||
Reference in New Issue
Block a user