# Sub-Pages — Modals for Table Tabs > **Part of:** [Sub-Pages guide](../sub-pages.md) > **See also:** [Table Tab](05-table-tab.md) | [Main Pages Delete Modal](../main-pages/08-delete-modal-and-utils.md) ## Overview Table tabs require Add and Delete modals for CRUD operations. Files to create: - `src/components/modals//AddModal.tsx` - `src/components/modals//DeleteModal.tsx` ## Add Modal ### Props Interface ```tsx interface AddModalProps { isOpen: boolean; onClose: () => void; onRefresh: () => void; : string; // e.g., dnsZoneId, idViewId } ``` ### Template ```tsx import React from "react"; import { Button, Form, FormGroup, Modal, ModalBody, ModalFooter, ModalHeader, Spinner, TextInput } from "@patternfly/react-core"; import { useAppDispatch } from "src/store/hooks"; import { addAlert } from "src/store/Global/alerts-slice"; import { useAddMutation } from "src/services/rpc"; const AddModal = (props: AddModalProps) => { const dispatch = useAppDispatch(); const [add] = useAddMutation(); const [field1, setField1] = React.useState(""); const [spinning, setSpinning] = React.useState(false); const resetFields = () => setField1(""); const onClose = () => { resetFields(); props.onClose(); }; const onAdd = () => { setSpinning(true); add({ : props., field1 }).then((response) => { if ("data" in response) { if (response.data?.error) { dispatch(addAlert({ name: "add-error", title: response.data.error.message, variant: "danger" })); } else { dispatch(addAlert({ name: "add-success", title: " added", variant: "success" })); props.onRefresh(); onClose(); } } setSpinning(false); }); }; return (
setField1(val)} />
); }; export default AddModal; ``` ## Delete Modal ### Props Interface The Delete modal also receives the parent entity ID: ```tsx interface DeleteModalProps { isOpen: boolean; onClose: () => void; elementsToDelete: []; clearSelectedElements: () => void; columnNames: string[]; keyNames: string[]; onRefresh: () => void; updateIsDeleteButtonDisabled: (value: boolean) => void; : string; } ``` ### Template ```tsx import React from "react"; import { Button, Content, ContentVariants, Spinner } from "@patternfly/react-core"; import { useAppDispatch } from "src/store/hooks"; import { addAlert } from "src/store/Global/alerts-slice"; import { useDeleteMutation } from "src/services/rpc"; import { BatchRPCResponse } from "src/services/rpc"; import ModalWithFormLayout from "src/components/layouts/ModalWithFormLayout"; import DeletedElementsTable from "src/components/tables/DeletedElementsTable"; const DeleteModal = (props: DeleteModalProps) => { const dispatch = useAppDispatch(); const [delete] = useDeleteMutation(); const [spinning, setSpinning] = React.useState(false); const onDelete = () => { setSpinning(true); const idsToDelete = props.elementsToDelete.map((el) => el.); delete({ : props., ids: idsToDelete }).then((response) => { if ("data" in response) { const data = response.data as BatchRPCResponse; if (data.result) { props.clearSelectedElements(); props.updateIsDeleteButtonDisabled(true); dispatch(addAlert({ name: "delete-success", title: "Items deleted", variant: "success" })); props.onRefresh(); props.onClose(); } } else if ("error" in response) { dispatch(addAlert({ name: "delete-error", title: "Delete failed", variant: "danger" })); } setSpinning(false); }); }; const modalActions = [ , , ]; return ( Are you sure you want to delete the selected items? } actions={modalActions} isOpen={props.isOpen} onClose={props.onClose} /> ); }; export default DeleteModal; ``` ## Examples | Modal Type | Example File | |------------|--------------| | Add (complex) | `src/components/modals/DnsZones/AddDnsRecordsModal.tsx` | | Add (simple) | `src/components/modals/DnsZones/AddDnsZoneModal.tsx` | | Delete | `src/components/modals/DnsZones/DeleteDnsRecords.tsx` |