diff --git a/vite/src/views/products/rewards/reward-config/DiscountConfig.tsx b/vite/src/views/products/rewards/reward-config/DiscountConfig.tsx index 7100175b2..db556f7fb 100644 --- a/vite/src/views/products/rewards/reward-config/DiscountConfig.tsx +++ b/vite/src/views/products/rewards/reward-config/DiscountConfig.tsx @@ -1,20 +1,13 @@ -import FieldLabel from "@/components/general/modal-components/FieldLabel"; -import { Input } from "@/components/ui/input"; -import { useState } from "react"; -import { Select, SelectContent, SelectItem } from "@/components/ui/select"; -import { SelectTrigger, SelectValue } from "@/components/ui/select"; -import { keyToTitle } from "@/utils/formatUtils/formatTextUtils"; import { - Reward, CouponDurationType, + type ProductItem, + type Reward, RewardType, - ProductItem, } from "@autumn/shared"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; +import { Check, ChevronsUpDown, X } from "lucide-react"; +import { useState } from "react"; +import FieldLabel from "@/components/general/modal-components/FieldLabel"; +import { Button } from "@/components/ui/button"; import { Command, CommandEmpty, @@ -23,104 +16,115 @@ import { CommandItem, CommandList, } from "@/components/ui/command"; -import { Check, ChevronsUpDown, Trash2, X } from "lucide-react"; - -import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; import { ScrollArea } from "@/components/ui/scroll-area"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useOrg } from "@/hooks/common/useOrg"; +import { useFeaturesQuery } from "@/hooks/queries/useFeaturesQuery"; +import { useProductsQuery } from "@/hooks/queries/useProductsQuery"; +import { keyToTitle } from "@/utils/formatUtils/formatTextUtils"; import { isFeatureItem } from "@/utils/product/getItemType"; import { formatProductItemText } from "@/utils/product/product-item/formatProductItem"; -import { useOrg } from "@/hooks/common/useOrg"; -import { useProductsQuery } from "@/hooks/queries/useProductsQuery"; -import { useFeaturesQuery } from "@/hooks/queries/useFeaturesQuery"; export const DiscountConfig = ({ - reward, - setReward, + reward, + setReward, }: { - reward: Reward; - setReward: (reward: Reward) => void; + reward: Reward; + setReward: (reward: Reward) => void; }) => { - const { org } = useOrg(); + const { org } = useOrg(); - const config = reward.discount_config!; - const setConfig = (key: any, value: any) => { - setReward({ - ...reward, - discount_config: { ...config, [key]: value }, - }); - }; + const config = reward.discount_config!; + const setConfig = (key: any, value: any) => { + setReward({ + ...reward, + discount_config: { ...config, [key]: value }, + }); + }; - return ( -
-
-
- Amount - - setConfig("discount_value", Number(e.target.value)) - } - endContent={ -

- {reward.type === RewardType.PercentageDiscount - ? "%" - : org?.default_currency || "USD"} -

- } - /> -
-
- Duration -
- {config.duration_type === CouponDurationType.Months && ( - { - setConfig("duration_value", Number(e.target.value)); - }} - type="number" - /> - )} - -
-
-
+ return ( +
+
+
+ Amount + + setConfig("discount_value", Number(e.target.value)) + } + endContent={ +

+ {reward.type === RewardType.PercentageDiscount + ? "%" + : org?.default_currency || "USD"} +

+ } + /> +
+
+ Duration +
+ {config.duration_type === CouponDurationType.Months && ( + { + setConfig("duration_value", Number(e.target.value)); + }} + type="number" + /> + )} + +
+
+
- {/* {config.duration_type !== CouponDurationType.OneOff && + {/* {config.duration_type !== CouponDurationType.OneOff && reward.type === RewardType.FixedDiscount && (
)} */} -
- {/*

Products

*/} - Products +
+ {/*

Products

*/} + Products - -
-
- ); + +
+
+ ); }; const ProductPriceSelector = ({ - reward, - setReward, + reward, + setReward, }: { - reward: Reward; - setReward: (reward: Reward) => void; + reward: Reward; + setReward: (reward: Reward) => void; }) => { - const { org } = useOrg(); - const { products } = useProductsQuery(); - const { features } = useFeaturesQuery(); + const { org } = useOrg(); + const { products } = useProductsQuery(); + const { features } = useFeaturesQuery(); - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(false); - const config = reward.discount_config!; - const setConfig = (key: any, value: any) => { - setReward({ - ...reward, - discount_config: { ...config, [key]: value }, - }); - }; + const config = reward.discount_config!; + const setConfig = (key: any, value: any) => { + setReward({ + ...reward, + discount_config: { ...config, [key]: value }, + }); + }; - // Handle selection/deselection of a price - const handlePriceToggle = (priceId: string) => { - let newPriceIds = [...(config.price_ids || [])]; - if (config.price_ids?.includes(priceId)) { - newPriceIds = config.price_ids?.filter((id) => id !== priceId) || []; - } else { - newPriceIds = [...(config.price_ids || []), priceId]; - } - setConfig("price_ids", newPriceIds); - }; + // Handle selection/deselection of a price + const handlePriceToggle = (priceId: string) => { + let newPriceIds = [...(config.price_ids || [])]; + if (config.price_ids?.includes(priceId)) { + newPriceIds = config.price_ids?.filter((id) => id !== priceId) || []; + } else { + newPriceIds = [...(config.price_ids || []), priceId]; + } + setConfig("price_ids", newPriceIds); + }; - if (!products || products.length === 0) { - return

No products available

; - } + if (!products || products.length === 0) { + return

No products available

; + } - return ( - - - -
- ); - })} - - )} - - - - - - - - - No prices found. - - { - setConfig("apply_to_all", !config.apply_to_all); - }} - className="cursor-pointer" - > -

Apply to all products

- {config.apply_to_all && ( - - )} -
-
- {!config.apply_to_all && - products.map((product: any) => ( - - {product.items.length > 0 ? ( - product.items - ?.filter((item: ProductItem) => { - return !isFeatureItem(item); - }) - .map((item: any) => ( - handlePriceToggle(item.price_id)} - className="cursor-pointer overflow-x-hidden max-w-[380px]" - > - - {formatProductItemText({ - item, - org, - features, - })} - + const text = item + ? formatProductItemText({ + item, + org, + features, + }) + : "Deleted price"; + return ( +
+

{text}

+ +
+ ); + })} + + + + + + + + + No prices found. + + { + setConfig("apply_to_all", !config.apply_to_all); + }} + className="cursor-pointer" + > +

Apply to all products

+ {config.apply_to_all && ( + + )} +
+
+ {!config.apply_to_all && + products.map((product: any) => ( + + {product.items.length > 0 ? ( + product.items + ?.filter((item: ProductItem) => { + return !isFeatureItem(item); + }) + .map((item: any) => ( + handlePriceToggle(item.price_id)} + className="cursor-pointer overflow-x-hidden max-w-[380px]" + > + + {formatProductItemText({ + item, + org, + features, + })} + - {config.price_ids?.includes(item.price_id) && ( - - )} - - )) - ) : ( - -

No prices available

-
- )} -
- ))} -
-
-
-
- - ); + {config.price_ids?.includes(item.price_id) && ( + + )} +
+ )) + ) : ( + +

No prices available

+
+ )} +
+ ))} +
+
+
+
+ + ); }; diff --git a/vite/src/views/products/rewards/reward-config/RewardConfig.tsx b/vite/src/views/products/rewards/reward-config/RewardConfig.tsx index c0feeb552..32caff09f 100644 --- a/vite/src/views/products/rewards/reward-config/RewardConfig.tsx +++ b/vite/src/views/products/rewards/reward-config/RewardConfig.tsx @@ -1,146 +1,153 @@ -import FieldLabel from "@/components/general/modal-components/FieldLabel"; - -import { Input } from "@/components/ui/input"; +import { + type ProductV2, + type Reward, + RewardType +} from "@autumn/shared"; import { useEffect, useState } from "react"; -import { Select, SelectContent, SelectItem } from "@/components/ui/select"; -import { SelectTrigger, SelectValue } from "@/components/ui/select"; -import { keyToTitle, slugify } from "@/utils/formatUtils/formatTextUtils"; -import { Reward, RewardType, Product, ProductV2 } from "@autumn/shared"; -import { useProductsContext } from "../../ProductsContext"; -import { DiscountConfig } from "./DiscountConfig"; -import { notNullish } from "@/utils/genUtils"; -import { defaultDiscountConfig } from "../utils/defaultRewardModels"; -import { isFreeProduct } from "@/utils/product/priceUtils"; +import FieldLabel from "@/components/general/modal-components/FieldLabel"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { useProductsQuery } from "@/hooks/queries/useProductsQuery"; +import { keyToTitle, slugify } from "@/utils/formatUtils/formatTextUtils"; +import { notNullish } from "@/utils/genUtils"; +import { isFreeProduct } from "@/utils/product/priceUtils"; +import { defaultDiscountConfig } from "../utils/defaultRewardModels"; +import { DiscountConfig } from "./DiscountConfig"; export const RewardConfig = ({ - reward, - setReward, + reward, + setReward, }: { - reward: Reward; - setReward: (reward: Reward) => void; + reward: Reward; + setReward: (reward: Reward) => void; }) => { - const [idChanged, setIdChanged] = useState(false); - const { products } = useProductsQuery(); + const [idChanged, setIdChanged] = useState(false); + const { products } = useProductsQuery(); - useEffect(() => { - if (!idChanged) { - setReward({ - ...reward, - id: slugify(reward.name || ""), - }); - } - }, [reward.name]); + useEffect(() => { + if (!idChanged) { + setReward({ + ...reward, + id: slugify(reward.name || ""), + }); + } + }, [idChanged, reward, setReward]); - return ( -
-
-
- Name - setReward({ ...reward, name: e.target.value })} - /> -
-
- - ID - - { - setReward({ ...reward, id: e.target.value }); - setIdChanged(true); - }} - /> -
-
-
-
- Promotional Code - 0 ? reward.promo_codes[0].code : "" - } - onChange={(e) => - setReward({ - ...reward, - promo_codes: [{ code: e.target.value }], - }) - } - /> -
-
- Type - -
-
- {reward.type === RewardType.FreeProduct ? ( -
- - Product - - setReward({ ...reward, name: e.target.value })} + /> +
+
+ + ID + + { + setReward({ ...reward, id: e.target.value }); + setIdChanged(true); + }} + /> +
+
+
+
+ Promotional Code + 0 ? reward.promo_codes[0].code : "" + } + onChange={(e) => + setReward({ + ...reward, + promo_codes: [{ code: e.target.value }], + }) + } + /> +
+
+ Type + +
+
+ {reward.type === RewardType.FreeProduct ? ( +
+ + Product + + -
- ) : notNullish(reward.type) ? ( - - ) : null} - - ); + const empty = freeAddOns.length === 0; + return ( + <> + + + + + {freeAddOns.map((product: ProductV2) => ( + + {product.name} + + ))} + + + ); + })()} + + + ) : notNullish(reward.type) ? ( + + ) : null} + + ); }; diff --git a/vite/src/views/products/rewards/utils/defaultRewardModels.ts b/vite/src/views/products/rewards/utils/defaultRewardModels.ts index 50b876616..0c913ea35 100644 --- a/vite/src/views/products/rewards/utils/defaultRewardModels.ts +++ b/vite/src/views/products/rewards/utils/defaultRewardModels.ts @@ -1,29 +1,29 @@ import { - CreateReward, - RewardType, CouponDurationType, - DiscountConfig, + type CreateReward, + type DiscountConfig, + RewardType, } from "@autumn/shared"; export const defaultDiscountConfig: DiscountConfig = { - discount_value: 0, - duration_type: CouponDurationType.Months, - duration_value: 0, - should_rollover: true, - apply_to_all: true, - price_ids: [], + discount_value: 0, + duration_type: CouponDurationType.Months, + duration_value: 0, + should_rollover: true, + apply_to_all: false, + price_ids: [], }; export const defaultReward: CreateReward = { - name: "", - id: "", - promo_codes: [{ code: "" }], + name: "", + id: "", + promo_codes: [{ code: "" }], - type: RewardType.PercentageDiscount, + type: RewardType.PercentageDiscount, - // For free product coupons - free_product_id: null, + // For free product coupons + free_product_id: null, - // For discount type coupons - discount_config: defaultDiscountConfig, + // For discount type coupons + discount_config: defaultDiscountConfig, };