bananas-commerce-admin
Version:
What's this, an admin for apes?
111 lines • 4.59 kB
JavaScript
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