From 70b4b30052e19d5e4586031e88e599f01701bc3c Mon Sep 17 00:00:00 2001 From: Winston Hsiao Date: Tue, 12 Nov 2024 19:16:17 -0500 Subject: [PATCH] type checking does not allow for dynamically constructed payment_methods --- frontend/src/components/pages/CreateSell.tsx | 561 +++++------------- .../src/components/stripe/CheckoutButton.tsx | 7 +- frontend/src/gen/api.ts | 14 +- frontend/src/lib/types/index.ts | 4 +- store/app/routers/listings.py | 53 +- store/app/routers/stripe.py | 46 +- 6 files changed, 238 insertions(+), 447 deletions(-) diff --git a/frontend/src/components/pages/CreateSell.tsx b/frontend/src/components/pages/CreateSell.tsx index 1cea195e..04ba64a9 100644 --- a/frontend/src/components/pages/CreateSell.tsx +++ b/frontend/src/components/pages/CreateSell.tsx @@ -27,111 +27,38 @@ const CreateSell = () => { const navigate = useNavigate(); const [description, setDescription] = useState(""); - const [slug, setSlug] = useState(""); - const [previewUrl, setPreviewUrl] = useState(""); const [images, setImages] = useState([]); const [displayPrice, setDisplayPrice] = useState("0.00"); const [isSubmitting, setIsSubmitting] = useState(false); - const [inventoryType, setInventoryType] = useState< - "infinite" | "finite" | "preorder" - >("infinite"); + const [slug, setSlug] = useState(""); + const [previewUrl, setPreviewUrl] = useState(""); + const [price_amount, setPriceAmount] = useState(0); const [isReservation, setIsReservation] = useState(false); const [displayDepositAmount, setDisplayDepositAmount] = useState("0.00"); - useEffect(() => { - if (!auth.currentUser?.stripe_connect_onboarding_completed) { - navigate(`${ROUTES.BOTS.path}/${ROUTES.BOTS.$.CREATE.relativePath}`); - } - }, [auth.currentUser, navigate]); - const { register, handleSubmit, formState: { errors }, watch, setValue, - trigger, } = useForm({ resolver: zodResolver(SellListingSchema), - mode: "onChange", - criteriaMode: "all", - shouldFocusError: true, defaultValues: { price_amount: 0, currency: "usd", - inventory_type: "infinite", + inventory_type: "finite", inventory_quantity: null, preorder_release_date: null, is_reservation: false, reservation_deposit_amount: null, }, - shouldUseNativeValidation: false, - reValidateMode: "onChange", - context: { - validate: (data: SellListingType) => { - const { - price_amount, - currency, - inventory_type, - inventory_quantity, - preorder_release_date, - is_reservation, - reservation_deposit_amount, - } = data; - if ((price_amount && !currency) || (!price_amount && currency)) { - return { - price_amount: - "Price amount and currency must be provided together.", - currency: "Price amount and currency must be provided together.", - }; - } - if ( - (inventory_type && !inventory_quantity) || - (!inventory_type && inventory_quantity) - ) { - return { - inventory_type: - "Inventory type and quantity must be provided together.", - inventory_quantity: - "Inventory type and quantity must be provided together.", - }; - } - if ( - (preorder_release_date && !is_reservation) || - (!preorder_release_date && is_reservation) - ) { - return { - preorder_release_date: - "Preorder release date and reservation must be provided together.", - is_reservation: - "Preorder release date and reservation must be provided together.", - }; - } - if ( - (is_reservation && !reservation_deposit_amount) || - (!is_reservation && reservation_deposit_amount) - ) { - return { - reservation_deposit_amount: - "Reservation deposit amount must be provided if reservation is true.", - is_reservation: - "Reservation deposit amount must be provided if reservation is true.", - }; - } - return {}; - }, - }, }); + const inventoryType = watch("inventory_type"); const name = watch("name"); - const price_amount = watch("price_amount"); - const currency = watch("currency"); - const inventory_quantity = watch("inventory_quantity"); - const preorder_release_date = watch("preorder_release_date"); - const reservation_deposit_amount = watch("reservation_deposit_amount"); - useEffect(() => { if (name) { const newSlug = slugify(name); @@ -142,119 +69,88 @@ const CreateSell = () => { useEffect(() => { if (auth.currentUser && slug) { - setPreviewUrl(`/bot/${auth.currentUser.username}/${slug}`); + setPreviewUrl( + `https://kscale.dev/bot/${auth.currentUser.username}/${slug}`, + ); } }, [auth.currentUser, slug]); - useEffect(() => { - if ((price_amount && !currency) || (!price_amount && currency)) { - trigger(["price_amount", "currency"]); - } - }, [price_amount, currency, trigger]); - - useEffect(() => { - if ( - (inventory_quantity && !inventoryType) || - (!inventory_quantity && inventoryType) - ) { - trigger(["inventory_quantity", "inventory_type"]); - } - }, [inventory_quantity, inventoryType, trigger]); - - useEffect(() => { - if ( - (preorder_release_date && !isReservation) || - (!preorder_release_date && isReservation) - ) { - trigger(["preorder_release_date", "is_reservation"]); - } - }, [preorder_release_date, isReservation, trigger]); - - useEffect(() => { - if ( - (isReservation && !reservation_deposit_amount) || - (!isReservation && reservation_deposit_amount) - ) { - trigger(["reservation_deposit_amount", "is_reservation"]); - } - }, [reservation_deposit_amount, isReservation, trigger]); - - const handleImageChange = (imageList: ImageListType) => { - setImages(imageList); - }; - const handlePriceChange = (e: React.ChangeEvent) => { const inputValue = e.target.value.replace(/[^0-9]/g, ""); - if (!inputValue) { + setPriceAmount(0); setDisplayPrice(""); - setValue("price_amount", null, { shouldValidate: true }); + setValue("price_amount", null); return; } - const decimalValue = convertToDecimal(inputValue); + setPriceAmount(parseFloat(decimalValue)); setDisplayPrice(decimalValue); - setValue("price_amount", parseFloat(decimalValue), { - shouldValidate: true, - }); + setValue("price_amount", parseFloat(decimalValue)); }; const handleDepositChange = (e: React.ChangeEvent) => { const inputValue = e.target.value.replace(/[^0-9]/g, ""); + setDisplayDepositAmount(inputValue); + setValue("reservation_deposit_amount", parseFloat(inputValue)); + }; - if (!inputValue) { - setDisplayDepositAmount(""); - setValue("reservation_deposit_amount", null, { shouldValidate: true }); - return; - } + const handleImageChange = (imageList: ImageListType) => { + setImages(imageList); + }; - const decimalValue = convertToDecimal(inputValue); - setDisplayDepositAmount(decimalValue); - setValue("reservation_deposit_amount", parseFloat(decimalValue), { - shouldValidate: true, - }); + const handleDateChange = (e: React.ChangeEvent) => { + const date = e.target.value; + if (date) { + // Convert the date to Unix timestamp in seconds + const timestamp = Math.floor(new Date(date).getTime() / 1000); + setValue("preorder_release_date", timestamp); + } else { + setValue("preorder_release_date", null); + } }; const onSubmit = async (data: SellListingType) => { - console.log("Submitting data:", data); setIsSubmitting(true); const formData = new FormData(); formData.append("name", data.name); formData.append("description", data.description || ""); - formData.append("slug", data.slug || slugify(data.name)); - formData.append( - "price_amount", - data.price_amount ? convertToCents(data.price_amount).toString() : "", - ); - formData.append("currency", data.currency); + formData.append("slug", slugify(data.name)); + + if (data.price_amount) { + formData.append( + "price_amount", + convertToCents(data.price_amount).toString(), + ); + } + + formData.append("currency", "usd"); formData.append("inventory_type", data.inventory_type); - formData.append( - "inventory_quantity", - data.inventory_quantity?.toString() || "", - ); - - // Convert preorder_release_date to a timestamp - const preorderReleaseDate = data.preorder_release_date - ? new Date(data.preorder_release_date).getTime() / 1000 - : null; - formData.append( - "preorder_release_date", - preorderReleaseDate ? preorderReleaseDate.toString() : "", - ); - - formData.append("is_reservation", data.is_reservation?.toString() || ""); - formData.append( - "reservation_deposit_amount", - data.reservation_deposit_amount - ? convertToCents(data.reservation_deposit_amount).toString() - : "", - ); - - // Append photos to formData + + if (data.inventory_type === "finite" && data.inventory_quantity) { + formData.append("inventory_quantity", data.inventory_quantity.toString()); + } + + if (data.inventory_type === "preorder" && data.preorder_release_date) { + formData.append( + "preorder_release_date", + data.preorder_release_date.toString(), + ); + } + + formData.append("is_reservation", data.is_reservation.toString()); + if (data.is_reservation && data.reservation_deposit_amount) { + formData.append( + "reservation_deposit_amount", + data.reservation_deposit_amount.toString(), + ); + } + + // Append photos images.forEach((image) => { if (image.file) { - formData.append(`photos`, image.file); + formData.append("photos", image.file); } }); @@ -268,7 +164,6 @@ const CreateSell = () => { ); if (error) { - console.error("Server error:", error.detail); addErrorAlert(`Failed to create listing: ${error.detail}`); return; } @@ -281,8 +176,6 @@ const CreateSell = () => { slug: responseData.slug, }), ); - } else { - throw new Error("Invalid response data"); } } catch (error) { console.error("Error creating listing:", error); @@ -292,60 +185,6 @@ const CreateSell = () => { } }; - const validateFields = () => { - if ((price_amount && !currency) || (!price_amount && currency)) { - return "Price amount and currency must be provided together or both left empty."; - } - if ( - (inventory_quantity && !inventoryType) || - (!inventory_quantity && inventoryType) - ) { - return "Inventory quantity and type must be provided together or both left empty."; - } - if ( - (preorder_release_date && !isReservation) || - (!preorder_release_date && isReservation) - ) { - return "Preorder release date and reservation must be provided together or both left empty."; - } - if ( - (isReservation && !reservation_deposit_amount) || - (!isReservation && reservation_deposit_amount) - ) { - return "Reservation deposit amount must be provided if reservation is true or both left empty."; - } - return true; - }; - - const handleInventoryTypeChange = ( - e: React.ChangeEvent, - ) => { - const newType = e.target.value as typeof inventoryType; - setInventoryType(newType); - setValue("inventory_type", newType); - - // Reset related fields based on inventory type - if (newType === "infinite") { - setValue("inventory_quantity", null); - setValue("preorder_release_date", null); - } else if (newType === "preorder") { - setValue("inventory_quantity", null); - setIsReservation(false); - setValue("is_reservation", false); - setValue("reservation_deposit_amount", null); - } else if (newType === "finite") { - setValue("preorder_release_date", null); - } - - // Trigger validation - trigger([ - "inventory_quantity", - "preorder_release_date", - "is_reservation", - "reservation_deposit_amount", - ]); - }; - return ( @@ -355,46 +194,38 @@ const CreateSell = () => {
{ - if (validateFields() !== true) { - addErrorAlert( - "Price amount and currency must be provided together or both left empty. Inventory quantity and type must be provided together or both left empty. Preorder release date and reservation must be provided together or both left empty. Reservation deposit amount must be provided if reservation is true or both left empty.", - ); - return; - } - onSubmit(data); - })} + onSubmit={handleSubmit(onSubmit)} className="grid grid-cols-1 space-y-6" > {/* Name */}
-
- {/* Description Input */} -
-