From 5c299362311eee7c9ed2d32cabfa1e4ec68c7519 Mon Sep 17 00:00:00 2001 From: tonym Date: Thu, 15 Jan 2026 00:10:04 -0600 Subject: [PATCH] Add prev/next navigation buttons to edit pages - Filament, spool, and vendor edit pages now have Prev/Next buttons - Navigate between items without returning to the list - Buttons are disabled at the beginning/end of the list Co-Authored-By: Claude Opus 4.5 --- client/public/locales/en/common.json | 4 +++ client/src/pages/filaments/edit.tsx | 52 +++++++++++++++++++++++++--- client/src/pages/spools/edit.tsx | 48 ++++++++++++++++++++++--- client/src/pages/vendors/edit.tsx | 52 +++++++++++++++++++++++++--- 4 files changed, 142 insertions(+), 14 deletions(-) diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index 22410a9..350c6a9 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -27,6 +27,10 @@ "unArchive": "Unarchive", "hideArchived": "Hide Archived", "showArchived": "Show Archived", + "prev": "Prev", + "next": "Next", + "yes": "Yes", + "no": "No", "notAccessTitle": "You don't have permission to access", "hideColumns": "Hide Columns", "clearFilters": "Clear Filters" diff --git a/client/src/pages/filaments/edit.tsx b/client/src/pages/filaments/edit.tsx index c89df6f..4d69fb5 100644 --- a/client/src/pages/filaments/edit.tsx +++ b/client/src/pages/filaments/edit.tsx @@ -1,9 +1,11 @@ +import { LeftOutlined, RightOutlined } from "@ant-design/icons"; import { Edit, useForm, useSelect } from "@refinedev/antd"; -import { HttpError, IResourceComponentsProps, useTranslate } from "@refinedev/core"; -import { Alert, ColorPicker, DatePicker, Form, Input, InputNumber, message, Radio, Select, Space, Typography } from "antd"; +import { HttpError, IResourceComponentsProps, useList, useTranslate } from "@refinedev/core"; +import { Alert, Button, ColorPicker, DatePicker, Form, Input, InputNumber, message, Radio, Select, Space, Typography } from "antd"; import TextArea from "antd/es/input/TextArea"; import dayjs from "dayjs"; -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; +import { useNavigate } from "react-router"; import { ExtraFieldFormItem, ParsedExtras, StringifiedExtras } from "../../components/extraFields"; import { MultiColorPicker } from "../../components/multiColorPicker"; import { formatNumberOnUserInput, numberParser, numberParserAllowEmpty } from "../../utils/parsing"; @@ -21,13 +23,21 @@ the form's onFinish method. Form.Item's normalize should do this, but it doesn't export const FilamentEdit: React.FC = () => { const t = useTranslate(); + const navigate = useNavigate(); const [messageApi, contextHolder] = message.useMessage(); const [hasChanged, setHasChanged] = useState(false); const extraFields = useGetFields(EntityType.filament); const currency = useCurrency(); const [colorType, setColorType] = useState<"single" | "multi">("single"); - const { formProps, saveButtonProps } = useForm({ + // Get all filament IDs for prev/next navigation + const { data: allFilaments } = useList({ + resource: "filament", + pagination: { mode: "off" }, + sorters: [{ field: "id", order: "asc" }], + }); + + const { formProps, saveButtonProps, id } = useForm({ liveMode: "manual", onLiveEvent() { // Warn the user if the filament has been updated since the form was opened @@ -36,6 +46,17 @@ export const FilamentEdit: React.FC = () => { }, }); + // Calculate prev/next IDs for navigation + const { prevId, nextId } = useMemo(() => { + if (!allFilaments?.data || !id) return { prevId: null, nextId: null }; + const ids = allFilaments.data.map((f) => f.id); + const currentIndex = ids.indexOf(Number(id)); + return { + prevId: currentIndex > 0 ? ids[currentIndex - 1] : null, + nextId: currentIndex < ids.length - 1 ? ids[currentIndex + 1] : null, + }; + }, [allFilaments?.data, id]); + // Get vendor selection options const { selectProps } = useSelect({ resource: "vendor", @@ -76,7 +97,28 @@ export const FilamentEdit: React.FC = () => { }; return ( - + ( + <> + + + {defaultButtons} + + )} + > {contextHolder}
= () => { const [searchParams, _] = useSearchParams(); const navigate = useNavigate(); - const { form, formProps, saveButtonProps } = useForm({ + // Get all spool IDs for prev/next navigation + const { data: allSpools } = useList({ + resource: "spool", + pagination: { mode: "off" }, + sorters: [{ field: "id", order: "asc" }], + }); + + const { form, formProps, saveButtonProps, id } = useForm({ liveMode: "manual", onLiveEvent() { // Warn the user if the spool has been updated since the form was opened @@ -60,6 +68,17 @@ export const SpoolEdit: React.FC = () => { const initialWeightValue = Form.useWatch("initial_weight", form); const spoolWeightValue = Form.useWatch("spool_weight", form); + // Calculate prev/next IDs for navigation + const { prevId, nextId } = useMemo(() => { + if (!allSpools?.data || !id) return { prevId: null, nextId: null }; + const ids = allSpools.data.map((s) => s.id); + const currentIndex = ids.indexOf(Number(id)); + return { + prevId: currentIndex > 0 ? ids[currentIndex - 1] : null, + nextId: currentIndex < ids.length - 1 ? ids[currentIndex + 1] : null, + }; + }, [allSpools?.data, id]); + // Add the filament_id field to the form if (formProps.initialValues) { formProps.initialValues["filament_id"] = formProps.initialValues["filament"].id; @@ -231,7 +250,28 @@ export const SpoolEdit: React.FC = () => { }, [initialUsedWeight]); return ( - + ( + <> + + + {defaultButtons} + + )} + > {contextHolder} = () => { const t = useTranslate(); + const navigate = useNavigate(); const [messageApi, contextHolder] = message.useMessage(); const [hasChanged, setHasChanged] = useState(false); const extraFields = useGetFields(EntityType.vendor); - const { formProps, saveButtonProps } = useForm({ + // Get all vendor IDs for prev/next navigation + const { data: allVendors } = useList({ + resource: "vendor", + pagination: { mode: "off" }, + sorters: [{ field: "id", order: "asc" }], + }); + + const { formProps, saveButtonProps, id } = useForm({ liveMode: "manual", onLiveEvent() { // Warn the user if the vendor has been updated since the form was opened @@ -30,6 +40,17 @@ export const VendorEdit: React.FC = () => { }, }); + // Calculate prev/next IDs for navigation + const { prevId, nextId } = useMemo(() => { + if (!allVendors?.data || !id) return { prevId: null, nextId: null }; + const ids = allVendors.data.map((v) => v.id); + const currentIndex = ids.indexOf(Number(id)); + return { + prevId: currentIndex > 0 ? ids[currentIndex - 1] : null, + nextId: currentIndex < ids.length - 1 ? ids[currentIndex + 1] : null, + }; + }, [allVendors?.data, id]); + // Parse the extra fields from string values into real types if (formProps.initialValues) { formProps.initialValues = ParsedExtras(formProps.initialValues); @@ -49,7 +70,28 @@ export const VendorEdit: React.FC = () => { }; return ( - + ( + <> + + + {defaultButtons} + + )} + > {contextHolder}