import React, { useCallback, useMemo, useState } from "react"; import { useParams } from "react-router-dom"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import Card from "@mui/material/Card"; import CardActions from "@mui/material/CardActions"; import Stack from "@mui/material/Stack"; import { useTheme } from "@mui/material/styles"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; import TableContainer from "@mui/material/TableContainer"; import TableRow from "@mui/material/TableRow"; import { TableCell } from "../../../components/Table/TableCell"; import TableHead from "../../../components/Table/TableHead"; import TableHeading from "../../../components/Table/TableHeading"; import TableCardHeader from "../../../components/TableCardHeader"; import { useApi } from "../../../contexts/ApiContext"; import { useDialog } from "../../../contexts/DialogContext"; import { useI18n } from "../../../contexts/I18nContext"; import { useUser } from "../../../contexts/UserContext"; import { hasPermission } from "../../../util/has_permission"; import { Price } from "../types/contrib"; import { PriceRow } from "./PriceRow"; export interface PricesCardProps { prices: Price[]; setPrices: (prices: Price[]) => void; } export const PricesCard: React.FC = ({ prices, setPrices }) => { const theme = useTheme(); const openDialog = useDialog(); const api = useApi(); const params = useParams(); const { t } = useI18n(); const { user } = useUser(); const [isEditing, setIsEditing] = useState(false); const [isDisabled, setIsDisabled] = useState(false); const [changedPrices, setChangedPrices] = useState>({}); const [deletedPrices, setDeletedPrices] = useState([]); const [createdPrices, setCreatedPrices] = useState([]); const hasChanges = useMemo( () => Object.entries(changedPrices).length > 0 || deletedPrices.length > 0 || createdPrices.length > 0, [changedPrices, deletedPrices, createdPrices], ); const shownPrices = useMemo(() => { if (!isEditing) return prices; return prices .map(({ amount, ...price }) => { if (deletedPrices.includes(price.article_code)) { return null; } return { ...price, amount: changedPrices[price.article_code] ?? amount, }; }) .filter((price): price is Price => price !== null); }, [prices, isEditing, changedPrices, deletedPrices]); const clear = useCallback(() => { setChangedPrices({}); setDeletedPrices([]); setCreatedPrices([]); setIsEditing(false); }, []); const save = useCallback(async () => { const action = api.operations["pricing.contrib:prices-update"]; if (!action) { throw new Error('Invalid action "pricing.contrib:prices-update".'); } setIsDisabled(true); const response = await action.call({ params, body: { updates: [ ...Object.entries(changedPrices).map(([article_code, amount]) => ({ article_code, amount, })), ...createdPrices, ], deleted: deletedPrices, }, }); setIsDisabled(false); if (response.ok) { const updatedPrices = await response.json(); setPrices(updatedPrices); clear(); } else { console.error("[PRICES_CARD]", response); } }, [changedPrices, deletedPrices, createdPrices]); return ( { if (hasChanges) { if ( await openDialog( "Unsaved changes", "You have unsaved changes. Are you sure you want to discard your changes?", { ok: "Discard changes", cancel: "Cancel" }, ) ) { clear(); } } else { setIsEditing(isEditing); } }} /> {t("Article number")} {t("Amount")} .MuiTableCell-root": { borderBottom: "none" }, }} > {shownPrices.length === 0 && ( {t("No prices")} )} {shownPrices.map((price, i) => ( { setChangedPrices((prev) => ({ ...prev, [price.article_code]: amount })); }} onDelete={() => { setDeletedPrices((prev) => [...prev, price.article_code]); }} /> ))} {isEditing && ( <> {createdPrices.map((price, i) => ( { setCreatedPrices((prev) => prev.map((p, j) => (i === j ? { ...p, amount } : p)), ); }} onChangeArticleCode={(article_code) => { setCreatedPrices((prev) => prev.map((p, j) => (i === j ? { ...p, article_code } : p)), ); }} onDelete={() => { setCreatedPrices((prev) => prev.filter((_, j) => j !== i)); }} /> ))} )}
{isEditing && ( )}
); };