All files / packages/app/features/receipt-components receipt-item-quantity-input.tsx

95% Statements 19/20
87.5% Branches 14/16
100% Functions 5/5
95% Lines 19/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97                                                912x         1291x 1291x 1291x 1291x   1291x       5x     3x       1291x 1291x   486x   1291x   1291x 18x             1273x     3453x           6x 6x 6x                                                  
import type React from "react";
 
import { useTranslation } from "react-i18next";
import { z } from "zod";
 
import { useTrpcMutationState } from "~app/hooks/use-trpc-mutation-state";
import { useAppForm } from "~app/utils/forms";
import { useTRPC } from "~app/utils/trpc";
import { quantitySchema, quantitySchemaDecimal } from "~app/utils/validation";
import { Spinner } from "~components/spinner";
import { Text } from "~components/text";
import { cn } from "~components/utils";
import { View } from "~components/view";
 
import { useActionsHooksContext, useReceiptContext } from "./context";
import { useCanEdit } from "./hooks";
import type { Item } from "./state";
 
type Props = {
	item: Item;
	isDisabled: boolean;
	className?: string;
};
 
export const ReceiptItemQuantityInput: React.FC<Props> = ({
	item,
	isDisabled: isExternalDisabled,
	className,
}) => {
	const { t } = useTranslation("receipts");
	const { receiptDisabled } = useReceiptContext();
	const { updateItemQuantity } = useActionsHooksContext();
	const canEdit = useCanEdit();
 
	const form = useAppForm({
		defaultValues: { value: item.quantity },
		validators: { onChange: z.object({ value: quantitySchema }) },
		onSubmit: ({ value }) => {
			Iif (value.value === item.quantity) {
				return;
			}
			updateItemQuantity(item.id, value.value);
		},
	});
 
	const trpc = useTRPC();
	const updateMutationState = useTrpcMutationState<"receiptItems.update">(
		trpc.receiptItems.update.mutationKey(),
		(vars) => vars.update.type === "quantity" && vars.id === item.id,
	);
	const isDisabled = receiptDisabled || isExternalDisabled;
 
	if (!canEdit) {
		return (
			<View className="flex-row items-center gap-1">
				<Text>{t("item.quantity", { quantity: item.quantity })}</Text>
			</View>
		);
	}
 
	return (
		<form.AppField name="value">
			{(field) => (
				<field.NumberField
					value={field.state.value}
					onValueChange={field.setValue}
					name={field.name}
 
					onBlur={() => {
						field.handleBlur();
						Eif (!isDisabled && updateMutationState?.status !== "pending") {
							void field.form.handleSubmit();
						}
					}}
					fieldError={
						field.state.meta.isDirty ? field.state.meta.errors : undefined
					}
					fractionDigits={quantitySchemaDecimal}
					aria-label={t("item.form.quantity.label")}
					mutation={updateMutationState}
					isDisabled={isDisabled}
					className={cn("shrink-0 basis-40", className)}
					labelPlacement="outside-left"
					endContent={
						<View className="flex-row gap-2">
							{updateMutationState?.status === "pending" ? (
								<Spinner size="sm" />
							) : null}
							<Text>{t("item.quantityPostfix")}</Text>
						</View>
					}
				/>
			)}
		</form.AppField>
	);
};