Added live updates to filaments in client
This commit is contained in:
@@ -207,6 +207,9 @@
|
|||||||
"list": "Filaments",
|
"list": "Filaments",
|
||||||
"show": "Show Filament",
|
"show": "Show Filament",
|
||||||
"show_title": "[Filament #{{id}}] {{name}}"
|
"show_title": "[Filament #{{id}}] {{name}}"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"filament_updated": "This filament has been updated by someone/something else since you opened this page. Saving will overwrite those changes!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vendor": {
|
"vendor": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { IResourceComponentsProps, useTranslate } from "@refinedev/core";
|
import { IResourceComponentsProps, useTranslate } from "@refinedev/core";
|
||||||
import { Edit, useForm, useSelect } from "@refinedev/antd";
|
import { Edit, useForm, useSelect } from "@refinedev/antd";
|
||||||
import { Form, Input, DatePicker, Select, InputNumber, ColorPicker } from "antd";
|
import { Form, Input, DatePicker, Select, InputNumber, ColorPicker, message, Alert } 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 { numberFormatter, numberParser } from "../../utils/parsing";
|
import { numberFormatter, numberParser } from "../../utils/parsing";
|
||||||
@@ -10,8 +10,17 @@ import { IFilament } from "./model";
|
|||||||
|
|
||||||
export const FilamentEdit: React.FC<IResourceComponentsProps> = () => {
|
export const FilamentEdit: React.FC<IResourceComponentsProps> = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
|
const [hasChanged, setHasChanged] = useState(false);
|
||||||
|
|
||||||
const { formProps, saveButtonProps } = useForm<IFilament>();
|
const { formProps, saveButtonProps } = useForm<IFilament>({
|
||||||
|
liveMode: "manual",
|
||||||
|
onLiveEvent() {
|
||||||
|
// Warn the user if the filament has been updated since the form was opened
|
||||||
|
messageApi.warning(t("filament.form.filament_updated"));
|
||||||
|
setHasChanged(true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const { selectProps } = useSelect<IVendor>({
|
const { selectProps } = useSelect<IVendor>({
|
||||||
resource: "vendor",
|
resource: "vendor",
|
||||||
@@ -24,6 +33,7 @@ export const FilamentEdit: React.FC<IResourceComponentsProps> = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Edit saveButtonProps={saveButtonProps}>
|
<Edit saveButtonProps={saveButtonProps}>
|
||||||
|
{contextHolder}
|
||||||
<Form {...formProps} layout="vertical">
|
<Form {...formProps} layout="vertical">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("filament.fields.id")}
|
label={t("filament.fields.id")}
|
||||||
@@ -237,6 +247,7 @@ export const FilamentEdit: React.FC<IResourceComponentsProps> = () => {
|
|||||||
<TextArea maxLength={1024} />
|
<TextArea maxLength={1024} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
{hasChanged && <Alert description={t("filament.form.filament_updated")} type="warning" showIcon />}
|
||||||
</Edit>
|
</Edit>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { IResourceComponentsProps, BaseRecord, useTranslate } from "@refinedev/core";
|
import { IResourceComponentsProps, BaseRecord, useTranslate, useInvalidate } from "@refinedev/core";
|
||||||
import { useTable, List, EditButton, ShowButton, CloneButton } from "@refinedev/antd";
|
import { useTable, List, EditButton, ShowButton, CloneButton } from "@refinedev/antd";
|
||||||
import { Table, Space, Button, Dropdown } from "antd";
|
import { Table, Space, Button, Dropdown } from "antd";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
useSpoolmanMaterials,
|
useSpoolmanMaterials,
|
||||||
useSpoolmanVendors,
|
useSpoolmanVendors,
|
||||||
} from "../../components/otherModels";
|
} from "../../components/otherModels";
|
||||||
|
import { useLiveify } from "../../components/liveify";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
@@ -28,6 +29,16 @@ interface IFilamentCollapsed extends Omit<IFilament, "vendor"> {
|
|||||||
"vendor.name": string | null;
|
"vendor.name": string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collapseFilament(element: IFilament): IFilamentCollapsed {
|
||||||
|
let vendor_name: string | null;
|
||||||
|
if (element.vendor) {
|
||||||
|
vendor_name = element.vendor.name;
|
||||||
|
} else {
|
||||||
|
vendor_name = null;
|
||||||
|
}
|
||||||
|
return { ...element, "vendor.name": vendor_name };
|
||||||
|
}
|
||||||
|
|
||||||
function translateColumnI18nKey(columnName: string): string {
|
function translateColumnI18nKey(columnName: string): string {
|
||||||
columnName = columnName.replace(".", "_");
|
columnName = columnName.replace(".", "_");
|
||||||
return `filament.fields.${columnName}`;
|
return `filament.fields.${columnName}`;
|
||||||
@@ -37,11 +48,15 @@ const namespace = "filamentList-v2";
|
|||||||
|
|
||||||
export const FilamentList: React.FC<IResourceComponentsProps> = () => {
|
export const FilamentList: React.FC<IResourceComponentsProps> = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
|
const invalidate = useInvalidate();
|
||||||
|
|
||||||
// Load initial state
|
// Load initial state
|
||||||
const initialState = useInitialTableState(namespace);
|
const initialState = useInitialTableState(namespace);
|
||||||
|
|
||||||
// Fetch data from the API
|
// Fetch data from the API
|
||||||
|
// To provide the live updates, we use a custom solution (useLiveify) instead of the built-in refine "liveMode" feature.
|
||||||
|
// This is because the built-in feature does not call the liveProvider subscriber with a list of IDs, but instead
|
||||||
|
// calls it with a list of filters, sorters, etc. This means the server-side has to support this, which is quite hard.
|
||||||
const { tableProps, sorters, setSorters, filters, setFilters, current, pageSize, setCurrent } = useTable<IFilament>({
|
const { tableProps, sorters, setSorters, filters, setFilters, current, pageSize, setCurrent } = useTable<IFilament>({
|
||||||
syncWithLocation: false,
|
syncWithLocation: false,
|
||||||
pagination: {
|
pagination: {
|
||||||
@@ -57,6 +72,16 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
|
|||||||
mode: "server",
|
mode: "server",
|
||||||
initial: initialState.filters,
|
initial: initialState.filters,
|
||||||
},
|
},
|
||||||
|
liveMode: "manual",
|
||||||
|
onLiveEvent(event) {
|
||||||
|
if (event.type === "created" || event.type === "deleted") {
|
||||||
|
// updated is handled by the liveify
|
||||||
|
invalidate({
|
||||||
|
resource: "filament",
|
||||||
|
invalidates: ["list"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create state for the columns to show
|
// Create state for the columns to show
|
||||||
@@ -92,19 +117,11 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
|
|||||||
useStoreInitialState(namespace, tableState);
|
useStoreInitialState(namespace, tableState);
|
||||||
|
|
||||||
// Collapse the dataSource to a mutable list and add a filament_name field
|
// Collapse the dataSource to a mutable list and add a filament_name field
|
||||||
const dataSource: IFilamentCollapsed[] = React.useMemo(
|
const queryDataSource: IFilamentCollapsed[] = React.useMemo(
|
||||||
() =>
|
() => (tableProps.dataSource ?? []).map(collapseFilament),
|
||||||
(tableProps.dataSource ?? []).map((element) => {
|
|
||||||
let vendor_name: string | null;
|
|
||||||
if (element.vendor) {
|
|
||||||
vendor_name = element.vendor.name;
|
|
||||||
} else {
|
|
||||||
vendor_name = null;
|
|
||||||
}
|
|
||||||
return { ...element, "vendor.name": vendor_name };
|
|
||||||
}),
|
|
||||||
[tableProps.dataSource]
|
[tableProps.dataSource]
|
||||||
);
|
);
|
||||||
|
const dataSource = useLiveify("filament", queryDataSource, collapseFilament);
|
||||||
|
|
||||||
if (tableProps.pagination) {
|
if (tableProps.pagination) {
|
||||||
tableProps.pagination.showSizeChanger = true;
|
tableProps.pagination.showSizeChanger = true;
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ const { Title } = Typography;
|
|||||||
export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
|
export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
|
|
||||||
const { queryResult } = useShow<IFilament>();
|
const { queryResult } = useShow<IFilament>({
|
||||||
|
liveMode: "auto",
|
||||||
|
});
|
||||||
const { data, isLoading } = queryResult;
|
const { data, isLoading } = queryResult;
|
||||||
|
|
||||||
const record = data?.data;
|
const record = data?.data;
|
||||||
|
|||||||
Reference in New Issue
Block a user