UNPKG

bananas-commerce-admin

Version:

What's this, an admin for apes?

111 lines 4.59 kB
import React, { useCallback, useMemo, useState } from "react"; import { Card as MuiCard, Grid2 as Grid, } from "@mui/material"; import { useSnackbar } from "notistack"; import { CardContext } from "../../contexts/CardContext"; import { useI18n } from "../../contexts/I18nContext"; import { ValidationError } from "../../util/form_validation"; export * from "./shared"; /** * A card component that wraps a form with `onSubmit` and provides `cardContext`. * This should be your building block for all admin forms not better represented by a Table. * Generic type `T` is used to type the `FormData` of `onSubmit`. * @example * ```tsx * interface MyCardValues { * name: string; * email: string; * } * * <Card<MyCardValues> * onSubmit={(values) => { * console.log(values); * return "Success!"; * }}> * <CardHeader title="My Card" /> * <CardContent> * <CardRow> * <TextField name="name" label="Name" /> * <TextField name="email" label="Email" /> * <CardRow/> * </CardContent> * <CardActions> * <CardCancelButton /> * <CardSaveButton /> * </CardActions> * </Card> */ export function Card({ alwaysEditable = false, children, columns = 1, defaultEditing = false, isCollapsible = false, isCompact = false, isDisabled: isDisabledDefault = false, isEditable, isOpen: isOpenDefault = false, onSubmit, sx, size, gridProps, ...props }) { isEditable ??= alwaysEditable; const { t } = useI18n(); const { enqueueSnackbar } = useSnackbar(); const [isEditing, setIsEditing] = useState(alwaysEditable || defaultEditing || false); const [hasChanges, setHasChanges] = useState(false); const [isDisabled, setIsDisabled] = useState(isDisabledDefault); const [isOpen, setIsOpen] = useState(isOpenDefault); const handleSubmit = useCallback(async (event) => { event.preventDefault(); if (onSubmit == null) throw new Error("No onSubmit function provided."); if (!isEditing && !alwaysEditable) throw new Error("Cannot submit when not in edit mode."); const form = event.currentTarget; const formData = new FormData(form); const values = Object.fromEntries(formData); setIsDisabled(true); try { const success = await onSubmit(values, event); if (success != null) { enqueueSnackbar(success, { variant: "success" }); } if (!alwaysEditable) { setIsEditing(false); } setIsDisabled(false); } catch (error) { // TODO Report error to sentry console.error("[CARD] Error when submitting data", error); if (error instanceof ValidationError) { const message = error.formErrors.at(0) ?? t("Invalid form input. Please correct the highlighted fields."); enqueueSnackbar(message, { variant: "error" }); } else { enqueueSnackbar(t("Failed to submit data. Contact support or try again later."), { variant: "error", }); } // Don't exit edit mode (and clear fields) on error. Give the user the chance to save/fix. setIsDisabled(false); } }, [onSubmit, enqueueSnackbar, t, isEditing, alwaysEditable]); const contextValues = useMemo(() => ({ alwaysEditable, columns, hasChanges, isCollapsible, isCompact, isDisabled, isEditable, isEditing, isOpen, setHasChanges, setIsEditing, setIsOpen, }), [isDisabled, isEditable, isEditing, columns, hasChanges, isOpen, isCompact, isCollapsible]); return (React.createElement(CardContext.Provider, { value: contextValues }, React.createElement(Grid, { size: size ?? 12, ...gridProps }, React.createElement(MuiCard, { component: isEditable || alwaysEditable ? "form" : "article", sx: { width: "100%", boxShadow: 0, borderRadius: 3, borderWidth: "1px", borderStyle: "solid", borderColor: (theme) => theme.palette.divider, bgcolor: (theme) => theme.palette.background.paper, overflow: "visible", ...sx, }, onSubmit: isEditable || isEditing ? handleSubmit : undefined, ...props }, children)))); } export default Card; //# sourceMappingURL=index.js.map