Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"use client";

import useGroup from "@/lib/swr/use-group";
import type { DiscountProps } from "@/lib/types";
import type { DiscountProps, GroupProps } from "@/lib/types";
import { DEFAULT_PARTNER_GROUP } from "@/lib/zod/schemas/groups";
import { useDiscountSheet } from "@/ui/partners/add-edit-discount-sheet";
import { ProgramRewardDescription } from "@/ui/partners/program-reward-description";
import { X } from "@/ui/shared/icons";
Expand All @@ -12,7 +13,7 @@ import {
Grid,
useLocalStorage,
} from "@dub/ui";
import { cn } from "@dub/utils";
import { cn, isClickOnInteractiveChild } from "@dub/utils";
import { motion } from "framer-motion";
import { BadgePercent } from "lucide-react";

Expand All @@ -23,16 +24,22 @@ export const GroupDiscount = () => {
<div>
<Banner />

{loading ? (
{loading || !group ? (
<DiscountSkeleton />
) : (
<DiscountItem discount={group?.discount} />
<DiscountItem discount={group?.discount} group={group} />
)}
</div>
);
};

const DiscountItem = ({ discount }: { discount?: DiscountProps | null }) => {
const DiscountItem = ({
discount,
group,
}: {
discount?: DiscountProps | null;
group: GroupProps;
}) => {
const { DiscountSheet, setIsOpen } = useDiscountSheet({
...(discount && { discount }),
});
Expand All @@ -42,16 +49,19 @@ const DiscountItem = ({ discount }: { discount?: DiscountProps | null }) => {
{DiscountSheet}
<div
className={cn(
"flex cursor-pointer items-center gap-4 rounded-lg p-6 transition-all",
"flex cursor-pointer flex-col gap-4 rounded-lg p-6 transition-all md:flex-row md:items-center",
discount && "border border-neutral-200 hover:border-neutral-300",
!discount && "bg-neutral-50 hover:bg-neutral-100",
)}
onClick={() => setIsOpen(true)}
onClick={(e) => {
if (isClickOnInteractiveChild(e)) return;
setIsOpen(true);
}}
>
<div className="flex size-10 items-center justify-center rounded-full border border-neutral-200 bg-white">
<BadgePercent className="size-4 text-neutral-600" />
</div>
<div className="flex flex-1 items-center justify-between">
<div className="flex flex-1 flex-col justify-between gap-y-4 md:flex-row md:items-center">
<div className="flex items-center gap-2">
<span className="text-sm font-normal">
{discount ? (
Expand All @@ -64,21 +74,52 @@ const DiscountItem = ({ discount }: { discount?: DiscountProps | null }) => {
</span>
</div>

<Button
text={discount ? "Edit" : "Create"}
variant={discount ? "secondary" : "primary"}
className="h-9 w-fit rounded-lg"
onClick={(e) => {
e.preventDefault();
setIsOpen(true);
}}
/>
<div className="flex flex-col-reverse items-center gap-2 md:flex-row">
{group.slug !== DEFAULT_PARTNER_GROUP.slug && (
<CopyDefaultDiscountButton />
)}
<Button
text={discount ? "Edit" : "Create"}
variant={discount ? "secondary" : "primary"}
className="h-9 w-full rounded-lg md:w-fit"
onClick={(e) => {
e.preventDefault();
setIsOpen(true);
}}
/>
</div>
</div>
</div>
</>
);
};

const CopyDefaultDiscountButton = () => {
const { group: defaultGroup } = useGroup({
slug: DEFAULT_PARTNER_GROUP.slug,
});

const { DiscountSheet, setIsOpen } = useDiscountSheet({
defaultDiscountValues: defaultGroup?.discount ?? undefined,
});

return defaultGroup?.discount ? (
<>
{DiscountSheet}
<Button
text="Duplicate default group"
variant="secondary"
className="animate-fade-in h-9 w-full rounded-lg md:w-fit"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setIsOpen(true);
}}
/>
</>
) : null;
};

const DiscountSkeleton = () => {
return (
<div className="flex items-center gap-4 rounded-lg bg-neutral-50 p-6">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import useGroup from "@/lib/swr/use-group";
import type { GroupProps, RewardProps } from "@/lib/types";
import { DEFAULT_PARTNER_GROUP } from "@/lib/zod/schemas/groups";
import { REWARD_EVENTS } from "@/ui/partners/constants";
import { ProgramRewardDescription } from "@/ui/partners/program-reward-description";
import {
Expand Down Expand Up @@ -145,16 +146,16 @@ const RewardItem = ({
}
scroll={false}
className={cn(
"flex items-center gap-4 rounded-lg p-6 transition-all",
"flex flex-col gap-4 rounded-lg p-6 transition-all md:flex-row md:items-center",
reward &&
"cursor-pointer border border-neutral-200 hover:border-neutral-300",
!reward && "bg-neutral-50 hover:bg-neutral-100",
)}
>
<div className="flex size-10 items-center justify-center rounded-full border border-neutral-200 bg-white">
<div className="flex size-10 shrink-0 items-center justify-center rounded-full border border-neutral-200 bg-white">
<Icon className="size-4 text-neutral-600" />
</div>
<div className="flex flex-1 items-center justify-between">
<div className="flex flex-1 flex-col justify-between gap-y-4 md:flex-row md:items-center">
<div className="flex items-center gap-2">
<span className="text-sm font-normal">
{reward ? (
Expand Down Expand Up @@ -187,23 +188,57 @@ const RewardItem = ({
}}
/>
) : (
<Button
text="Create"
variant="primary"
className="h-9 w-fit rounded-lg"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setIsOpen(true);
}}
/>
<div className="flex flex-col-reverse items-center gap-2 md:flex-row">
{group.slug !== DEFAULT_PARTNER_GROUP.slug && (
<CopyDefaultRewardButton event={event} />
)}
<Button
text="Create"
variant="primary"
className="h-9 w-full rounded-lg md:w-fit"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setIsOpen(true);
}}
/>
</div>
)}
</div>
</As>
</>
);
};

const CopyDefaultRewardButton = ({ event }: { event: EventType }) => {
const { group: defaultGroup } = useGroup({
slug: DEFAULT_PARTNER_GROUP.slug,
});

const defaultReward = defaultGroup?.[`${event}Reward`];

const { RewardSheet, setIsOpen } = useRewardSheet({
event,
defaultRewardValues: defaultReward ?? undefined,
});

return defaultReward ? (
<>
{RewardSheet}
<Button
text="Duplicate default group"
variant="secondary"
className="animate-fade-in h-9 w-full rounded-lg md:w-fit"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setIsOpen(true);
}}
/>
</>
) : null;
};

const RewardSkeleton = () => {
return (
<div className="flex items-center gap-4 rounded-lg bg-neutral-50 p-6">
Expand Down
25 changes: 18 additions & 7 deletions apps/web/ui/partners/add-edit-discount-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
interface DiscountSheetProps {
setIsOpen: Dispatch<SetStateAction<boolean>>;
discount?: DiscountProps;
defaultDiscountValues?: DiscountProps;
}

type FormData = z.infer<typeof createDiscountSchema>;
Expand All @@ -46,14 +47,20 @@ const discountTypes = [
},
] as const;

function DiscountSheetContent({ setIsOpen, discount }: DiscountSheetProps) {
function DiscountSheetContent({
setIsOpen,
discount,
defaultDiscountValues,
}: DiscountSheetProps) {
const formRef = useRef<HTMLFormElement>(null);
const { id: workspaceId, defaultProgramId } = useWorkspace();
const { mutate: mutateProgram } = useProgram();
const { group, mutateGroup } = useGroup();

const defaultValuesSource = discount || defaultDiscountValues;

const [isRecurring, setIsRecurring] = useState(
discount ? discount.maxDuration !== 0 : false,
defaultValuesSource ? defaultValuesSource.maxDuration !== 0 : false,
);

const [accordionValues, setAccordionValues] = useState<string[]>([
Expand All @@ -70,12 +77,16 @@ function DiscountSheetContent({ setIsOpen, discount }: DiscountSheetProps) {
} = useForm<FormData>({
defaultValues: {
amount:
discount?.type === "flat" ? discount.amount / 100 : discount?.amount,
type: discount?.type || "percentage",
defaultValuesSource?.type === "flat"
? defaultValuesSource.amount / 100
: defaultValuesSource?.amount,
type: defaultValuesSource?.type || "percentage",
maxDuration:
discount?.maxDuration === null ? Infinity : discount?.maxDuration || 0,
couponId: discount?.couponId || "",
couponTestId: discount?.couponTestId || "",
defaultValuesSource?.maxDuration === null
? Infinity
: defaultValuesSource?.maxDuration || 0,
couponId: defaultValuesSource?.couponId || "",
couponTestId: defaultValuesSource?.couponTestId || "",
},
});

Expand Down
29 changes: 21 additions & 8 deletions apps/web/ui/partners/rewards/add-edit-reward-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ interface RewardSheetProps {
setIsOpen: Dispatch<SetStateAction<boolean>>;
event: EventType;
reward?: RewardProps;
defaultRewardValues?: RewardProps;
}

// Special form schema to allow for empty condition fields when adding a new condition
Expand All @@ -73,25 +74,37 @@ type FormData = z.infer<typeof formSchema>;

export const useAddEditRewardForm = () => useFormContext<FormData>();

function RewardSheetContent({ setIsOpen, event, reward }: RewardSheetProps) {
function RewardSheetContent({
setIsOpen,
event,
reward,
defaultRewardValues,
}: RewardSheetProps) {
const { group, mutateGroup } = useGroup();
const { id: workspaceId, defaultProgramId, plan } = useWorkspace();
const formRef = useRef<HTMLFormElement>(null);
const { mutate: mutateProgram } = useProgram();

const defaultValuesSource = reward || defaultRewardValues;

const form = useForm<FormData>({
defaultValues: {
event,
type: reward?.type || (event === "sale" ? "percentage" : "flat"),
maxDuration: reward
? reward.maxDuration === null
type:
defaultValuesSource?.type || (event === "sale" ? "percentage" : "flat"),
maxDuration: defaultValuesSource
? defaultValuesSource.maxDuration === null
? Infinity
: reward.maxDuration
: defaultValuesSource.maxDuration
: Infinity,
amount: reward?.type === "flat" ? reward.amount / 100 : reward?.amount,
modifiers: reward?.modifiers?.map((m) => ({
amount:
defaultValuesSource?.type === "flat"
? defaultValuesSource.amount / 100
: defaultValuesSource?.amount,
modifiers: defaultValuesSource?.modifiers?.map((m) => ({
...m,
amount: reward?.type === "flat" ? m.amount / 100 : m.amount,
amount:
defaultValuesSource?.type === "flat" ? m.amount / 100 : m.amount,
})),
},
});
Expand Down