diff --git a/frontend/src/components/modals/CancelOrderModal.tsx b/frontend/src/components/modals/CancelOrderModal.tsx index 726d9fac..80e390eb 100644 --- a/frontend/src/components/modals/CancelOrderModal.tsx +++ b/frontend/src/components/modals/CancelOrderModal.tsx @@ -42,7 +42,7 @@ const CancelOrderModal: React.FC = ({ const MAX_REASON_LENGTH = 500; const [cancellation, setCancellation] = useState({ - payment_intent_id: order["stripe_payment_intent_id"], + payment_intent_id: order["stripe_payment_intent_id"] || "", cancel_reason: { reason: "", details: "" }, amount: order["amount"], }); @@ -79,6 +79,12 @@ const CancelOrderModal: React.FC = ({ const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + + if (!cancellation.payment_intent_id) { + addErrorAlert("Invalid payment information"); + return; + } + try { const { data, error } = await client.PUT("/stripe/refunds/{order_id}", { params: { path: { order_id: order.id } }, diff --git a/frontend/src/components/pages/CreateSell.tsx b/frontend/src/components/pages/CreateSell.tsx index 1cea195e..7c1097ce 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,109 +185,47 @@ 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 ( -
+
{ - 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 */} -
-