Merge pull request #172 from clevelandcs/master
#76 spool list, no reload on page change
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
"buttons": {
|
"buttons": {
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
|
"saveAndAdd": "Save and Add",
|
||||||
"logout": "Log out",
|
"logout": "Log out",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
@@ -190,7 +191,8 @@
|
|||||||
"comment": "Comment",
|
"comment": "Comment",
|
||||||
"settings_extruder_temp": "Extruder Temp",
|
"settings_extruder_temp": "Extruder Temp",
|
||||||
"settings_bed_temp": "Bed Temp",
|
"settings_bed_temp": "Bed Temp",
|
||||||
"color_hex": "Color"
|
"color_hex": "Color",
|
||||||
|
"spools": "Show Spools"
|
||||||
},
|
},
|
||||||
"fields_help": {
|
"fields_help": {
|
||||||
"name": "Filament name, to distinguish this filament type among others from the same vendor. Should contain the color for example.",
|
"name": "Filament name, to distinguish this filament type among others from the same vendor. Should contain the color for example.",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { IResourceComponentsProps, useTranslate } from "@refinedev/core";
|
import { IResourceComponentsProps, useTranslate } from "@refinedev/core";
|
||||||
import { Create, useForm, useSelect } from "@refinedev/antd";
|
import { Create, useForm, useSelect } from "@refinedev/antd";
|
||||||
import { Form, Input, Select, InputNumber, ColorPicker } from "antd";
|
import { Form, Input, Select, InputNumber, ColorPicker, Button } 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";
|
||||||
@@ -14,7 +14,7 @@ interface CreateOrCloneProps {
|
|||||||
export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps> = (props) => {
|
export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps> = (props) => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
|
|
||||||
const { formProps, saveButtonProps, formLoading } = useForm<IFilament>();
|
const { form, formProps, saveButtonProps, formLoading, onFinish, redirect } = useForm<IFilament>();
|
||||||
|
|
||||||
if (props.mode === "clone" && formProps.initialValues) {
|
if (props.mode === "clone" && formProps.initialValues) {
|
||||||
// Fix the vendor_id
|
// Fix the vendor_id
|
||||||
@@ -23,6 +23,12 @@ export const FilamentCreate: React.FC<IResourceComponentsProps & CreateOrClonePr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (redirectTo: "list" | "edit" | "create") => {
|
||||||
|
let values = await form.validateFields();
|
||||||
|
await onFinish(values);
|
||||||
|
redirect(redirectTo, (values as IFilament).id);
|
||||||
|
}
|
||||||
|
|
||||||
const { selectProps } = useSelect<IVendor>({
|
const { selectProps } = useSelect<IVendor>({
|
||||||
resource: "vendor",
|
resource: "vendor",
|
||||||
optionLabel: "name",
|
optionLabel: "name",
|
||||||
@@ -32,7 +38,12 @@ 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}
|
||||||
saveButtonProps={saveButtonProps}
|
footerButtons={() => (
|
||||||
|
<>
|
||||||
|
<Button type="primary" onClick={() => handleSubmit("list")}>{t("buttons.save")}</Button>
|
||||||
|
<Button type="primary" onClick={() => handleSubmit("create")}>{t("buttons.saveAndAdd")}</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Form {...formProps} layout="vertical">
|
<Form {...formProps} layout="vertical">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import utc from "dayjs/plugin/utc";
|
|||||||
import { IFilament } from "./model";
|
import { IFilament } from "./model";
|
||||||
import { enrichText } from "../../utils/parsing";
|
import { enrichText } from "../../utils/parsing";
|
||||||
import { IVendor } from "../vendors/model";
|
import { IVendor } from "../vendors/model";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
||||||
export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
|
export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
|
const navigate = useNavigate();
|
||||||
const { queryResult } = useShow<IFilament>({
|
const { queryResult } = useShow<IFilament>({
|
||||||
liveMode: "auto",
|
liveMode: "auto",
|
||||||
});
|
});
|
||||||
@@ -31,17 +31,24 @@ export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
|
|||||||
return t("filament.titles.show_title", { id: item.id, name: vendorPrefix + item.name });
|
return t("filament.titles.show_title", { id: item.id, name: vendorPrefix + item.name });
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatVendor = (item: IVendor) => {
|
const gotoVendor = (): undefined => {
|
||||||
const URL = `/vendor/show/${item.id}`;
|
const URL = `/vendor/show/${record?.vendor?.id}`;
|
||||||
return <a href={URL}>{item.name}</a>;
|
navigate(URL);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
const gotoSpools = (): undefined => {
|
||||||
|
const URL = `/spool#filters=[{"field":"filament.id","operator":"in","value":[${record?.id}]}]`
|
||||||
|
navigate(URL);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show isLoading={isLoading} title={record ? formatTitle(record) : ""}>
|
<Show isLoading={isLoading} title={record ? formatTitle(record) : ""}>
|
||||||
<Title level={5}>{t("filament.fields.id")}</Title>
|
<Title level={5}>{t("filament.fields.id")}</Title>
|
||||||
<NumberField value={record?.id ?? ""} />
|
<NumberField value={record?.id ?? ""} />
|
||||||
<Title level={5}>{t("filament.fields.vendor")}</Title>
|
<Title level={5}>{t("filament.fields.vendor")}</Title>
|
||||||
<TextField value={record?.vendor ? formatVendor(record?.vendor) : ""} />
|
<button onClick={gotoVendor} style={{ background: 'none', border: 'none', color: 'blue', cursor: 'pointer', paddingLeft: 0 }}>
|
||||||
|
{record ? record.vendor?.name : ""}
|
||||||
|
</button>
|
||||||
<Title level={5}>{t("filament.fields.registered")}</Title>
|
<Title level={5}>{t("filament.fields.registered")}</Title>
|
||||||
<DateField
|
<DateField
|
||||||
value={dayjs.utc(record?.registered).local()}
|
value={dayjs.utc(record?.registered).local()}
|
||||||
@@ -110,7 +117,11 @@ export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
|
|||||||
<TextField value={record?.article_number} />
|
<TextField value={record?.article_number} />
|
||||||
<Title level={5}>{t("filament.fields.comment")}</Title>
|
<Title level={5}>{t("filament.fields.comment")}</Title>
|
||||||
<TextField value={enrichText(record?.comment)} />
|
<TextField value={enrichText(record?.comment)} />
|
||||||
</Show>
|
<Title level={5}>{t("filament.fields.spools")}</Title>
|
||||||
|
<button onClick={gotoSpools} style={{ background: 'none', border: 'none', color: 'blue', cursor: 'pointer', paddingLeft: 0 }}>
|
||||||
|
{record ? formatTitle(record) : ""}
|
||||||
|
</button>
|
||||||
|
</Show >
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { IResourceComponentsProps, useTranslate } from "@refinedev/core";
|
import { IResourceComponentsProps, useTranslate } from "@refinedev/core";
|
||||||
import { Create, useForm, useSelect } from "@refinedev/antd";
|
import { Create, useForm, useSelect } from "@refinedev/antd";
|
||||||
import { Form, Input, DatePicker, Select, InputNumber, Radio, Divider } from "antd";
|
import { Form, Input, DatePicker, Select, InputNumber, Radio, Divider, Button } from "antd";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import TextArea from "antd/es/input/TextArea";
|
import TextArea from "antd/es/input/TextArea";
|
||||||
import { IFilament } from "../filaments/model";
|
import { IFilament } from "../filaments/model";
|
||||||
import { ISpool } from "./model";
|
import { ISpool } from "./model";
|
||||||
import { numberFormatter, numberParser } from "../../utils/parsing";
|
import { numberFormatter, numberParser } from "../../utils/parsing";
|
||||||
import { useSpoolmanLocations } from "../../components/otherModels";
|
import { useSpoolmanLocations } from "../../components/otherModels";
|
||||||
|
import { MinusOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
|
import '../../utils/overrides.css'
|
||||||
|
|
||||||
interface CreateOrCloneProps {
|
interface CreateOrCloneProps {
|
||||||
mode: "create" | "clone";
|
mode: "create" | "clone";
|
||||||
@@ -16,7 +18,10 @@ interface CreateOrCloneProps {
|
|||||||
export const SpoolCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps> = (props) => {
|
export const SpoolCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps> = (props) => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
|
|
||||||
const { form, formProps, saveButtonProps, formLoading } = useForm<ISpool>();
|
const { form, formProps, formLoading, onFinish, redirect } = useForm<ISpool>({
|
||||||
|
redirect: false,
|
||||||
|
warnWhenUnsavedChanges: false
|
||||||
|
});
|
||||||
|
|
||||||
if (props.mode === "clone" && formProps.initialValues) {
|
if (props.mode === "clone" && formProps.initialValues) {
|
||||||
// Clear out the values that we don't want to clone
|
// Clear out the values that we don't want to clone
|
||||||
@@ -28,6 +33,18 @@ export const SpoolCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps
|
|||||||
formProps.initialValues.filament_id = formProps.initialValues.filament.id;
|
formProps.initialValues.filament_id = formProps.initialValues.filament.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (redirectTo: "list" | "edit" | "create") => {
|
||||||
|
let values = await form.validateFields();
|
||||||
|
if (quantity > 1) {
|
||||||
|
let submit = Array(quantity).fill(values);
|
||||||
|
// queue multiple creates this way for now Refine doesn't seem to map Arrays to createMany or multiple creates like it says it does
|
||||||
|
submit.forEach(async r => await onFinish(r));
|
||||||
|
} else {
|
||||||
|
await onFinish(values);
|
||||||
|
}
|
||||||
|
redirect(redirectTo, (values as ISpool).id);
|
||||||
|
}
|
||||||
|
|
||||||
const { queryResult } = useSelect<IFilament>({
|
const { queryResult } = useSelect<IFilament>({
|
||||||
resource: "filament",
|
resource: "filament",
|
||||||
});
|
});
|
||||||
@@ -100,11 +117,42 @@ export const SpoolCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps
|
|||||||
allLocations.push(newLocation.trim());
|
allLocations.push(newLocation.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [quantity, setQuantity] = useState(1);
|
||||||
|
const incrementQty = () => {
|
||||||
|
setQuantity(quantity + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const decrementQty = () => {
|
||||||
|
setQuantity(quantity - 1);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Create
|
<Create
|
||||||
title={props.mode === "create" ? t("spool.titles.create") : t("spool.titles.clone")}
|
title={props.mode === "create" ? t("spool.titles.create") : t("spool.titles.clone")}
|
||||||
saveButtonProps={saveButtonProps}
|
|
||||||
isLoading={formLoading}
|
isLoading={formLoading}
|
||||||
|
footerButtons={() => (
|
||||||
|
<>
|
||||||
|
<div style={{ display: 'flex', backgroundColor: '#141414', border: '1px solid #424242', borderRadius: '6px' }}>
|
||||||
|
<Button type="text" style={{ padding: 0, width: 32, height: 32 }} onClick={decrementQty}>
|
||||||
|
<MinusOutlined />
|
||||||
|
</Button>
|
||||||
|
<InputNumber
|
||||||
|
name="Quantity"
|
||||||
|
min={1}
|
||||||
|
id="qty-input"
|
||||||
|
controls={false}
|
||||||
|
value={quantity}
|
||||||
|
>
|
||||||
|
</InputNumber>
|
||||||
|
<Button type="text" style={{ padding: 0, width: 32, height: 32 }} onClick={incrementQty}>
|
||||||
|
<PlusOutlined />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Button type="primary" onClick={() => handleSubmit("list")}>{t("buttons.save")}</Button>
|
||||||
|
<Button type="primary" onClick={() => handleSubmit("create")}>{t("buttons.saveAndAdd")}</Button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Form {...formProps} layout="vertical">
|
<Form {...formProps} layout="vertical">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -278,7 +326,7 @@ export const SpoolCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps
|
|||||||
<TextArea maxLength={1024} />
|
<TextArea maxLength={1024} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</Create>
|
</Create >
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
17
client/src/pages/vendors/create.tsx
vendored
17
client/src/pages/vendors/create.tsx
vendored
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { IResourceComponentsProps, useTranslate } from "@refinedev/core";
|
import { IResourceComponentsProps, useTranslate } from "@refinedev/core";
|
||||||
import { Create, useForm } from "@refinedev/antd";
|
import { Create, useForm } from "@refinedev/antd";
|
||||||
import { Form, Input } from "antd";
|
import { Button, Form, Input } from "antd";
|
||||||
import TextArea from "antd/es/input/TextArea";
|
import TextArea from "antd/es/input/TextArea";
|
||||||
import { IVendor } from "./model";
|
import { IVendor } from "./model";
|
||||||
|
|
||||||
@@ -12,13 +12,24 @@ interface CreateOrCloneProps {
|
|||||||
export const VendorCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps> = (props) => {
|
export const VendorCreate: React.FC<IResourceComponentsProps & CreateOrCloneProps> = (props) => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
|
|
||||||
const { formProps, saveButtonProps, formLoading } = useForm<IVendor>();
|
const { form, formProps, saveButtonProps, formLoading, onFinish, redirect } = useForm<IVendor>();
|
||||||
|
|
||||||
|
const handleSubmit = async (redirectTo: "list" | "edit" | "create") => {
|
||||||
|
let values = await form.validateFields();
|
||||||
|
await onFinish(values);
|
||||||
|
redirect(redirectTo, (values as IVendor).id);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Create
|
<Create
|
||||||
title={props.mode === "create" ? t("vendor.titles.create") : t("vendor.titles.clone")}
|
title={props.mode === "create" ? t("vendor.titles.create") : t("vendor.titles.clone")}
|
||||||
isLoading={formLoading}
|
isLoading={formLoading}
|
||||||
saveButtonProps={saveButtonProps}
|
footerButtons={() => (
|
||||||
|
<>
|
||||||
|
<Button type="primary" onClick={() => handleSubmit("list")}>{t("buttons.save")}</Button>
|
||||||
|
<Button type="primary" onClick={() => handleSubmit("create")}>{t("buttons.saveAndAdd")}</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Form {...formProps} layout="vertical">
|
<Form {...formProps} layout="vertical">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|||||||
3
client/src/utils/overrides.css
Normal file
3
client/src/utils/overrides.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#qty-input {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
@@ -18,18 +18,18 @@ export function useInitialTableState(tableId: string): TableState {
|
|||||||
const savedSorters = hasHashProperty("sorters")
|
const savedSorters = hasHashProperty("sorters")
|
||||||
? getHashProperty("sorters")
|
? getHashProperty("sorters")
|
||||||
: isLocalStorageAvailable
|
: isLocalStorageAvailable
|
||||||
? localStorage.getItem(`${tableId}-sorters`)
|
? localStorage.getItem(`${tableId}-sorters`)
|
||||||
: null;
|
: null;
|
||||||
const savedFilters = hasHashProperty("filters")
|
const savedFilters = hasHashProperty("filters")
|
||||||
? getHashProperty("filters")
|
? getHashProperty("filters")
|
||||||
: isLocalStorageAvailable
|
: isLocalStorageAvailable
|
||||||
? localStorage.getItem(`${tableId}-filters`)
|
? localStorage.getItem(`${tableId}-filters`)
|
||||||
: null;
|
: null;
|
||||||
const savedPagination = hasHashProperty("pagination")
|
const savedPagination = hasHashProperty("pagination")
|
||||||
? getHashProperty("pagination")
|
? getHashProperty("pagination")
|
||||||
: isLocalStorageAvailable
|
: isLocalStorageAvailable
|
||||||
? localStorage.getItem(`${tableId}-pagination`)
|
? localStorage.getItem(`${tableId}-pagination`)
|
||||||
: null;
|
: null;
|
||||||
const savedShowColumns = isLocalStorageAvailable ? localStorage.getItem(`${tableId}-showColumns`) : null;
|
const savedShowColumns = isLocalStorageAvailable ? localStorage.getItem(`${tableId}-showColumns`) : null;
|
||||||
|
|
||||||
const sorters = savedSorters ? JSON.parse(savedSorters) : [{ field: "id", order: "asc" }];
|
const sorters = savedSorters ? JSON.parse(savedSorters) : [{ field: "id", order: "asc" }];
|
||||||
@@ -59,7 +59,7 @@ export function useStoreInitialState(tableId: string, state: TableState) {
|
|||||||
if (filters.length > 0) {
|
if (filters.length > 0) {
|
||||||
if (isLocalStorageAvailable) {
|
if (isLocalStorageAvailable) {
|
||||||
localStorage.setItem(`${tableId}-filters`, JSON.stringify(filters));
|
localStorage.setItem(`${tableId}-filters`, JSON.stringify(filters));
|
||||||
setURLHash("filters", JSON.stringify(state.filters));
|
setURLHash("filters", JSON.stringify(filters));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
localStorage.removeItem(`${tableId}-filters`);
|
localStorage.removeItem(`${tableId}-filters`);
|
||||||
|
|||||||
Reference in New Issue
Block a user