@coursebuilder/commerce-next
Version:
Commerce Functionality for Course Builder with Next.js
99 lines (98 loc) • 6.18 kB
JavaScript
'use client';
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { Clipboard } from 'lucide-react';
import pluralize from 'pluralize';
import { useCopyToClipboard } from 'react-use';
import { Alert, Button, Input } from '@coursebuilder/ui';
import { useToast } from '@coursebuilder/ui/primitives/use-toast';
import { cn } from '@coursebuilder/ui/utils/cn';
import { handleSelfRedeem } from '../utils/handle-self-redeem';
export default function InviteTeam(props) {
return (_jsxs(Root, { ...props, children: [_jsx(SeatsAvailable, { className: "[&_span]:font-semibold" }), _jsx("p", { children: "Send the following invite link to your colleagues to get started:" }), _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(InviteLink, {}), _jsx(CopyInviteLinkButton, {})] }), _jsx(SelfRedeemButton, {})] }));
}
const InviteTeamContext = React.createContext(undefined);
export const InviteTeamProvider = ({ children, ...props }) => {
return (_jsx(InviteTeamContext.Provider, { value: props, children: children }));
};
export const useInviteTeam = () => {
const context = React.use(InviteTeamContext);
if (context === undefined) {
throw new Error('useInviteTeam must be used within an InviteTeamProvider');
}
return context;
};
const Root = ({ children, className, ...props }) => {
const code = props.purchase?.bulkCoupon?.id;
const inviteLink = `${process.env.NEXT_PUBLIC_URL}?code=${code}`;
return (_jsx(InviteTeamProvider, { ...props, inviteLink: inviteLink, children: _jsx("section", { className: cn('w-full', className), children: children }) }));
};
const SeatsAvailable = ({ children, asChild, className }) => {
const { purchase } = useInviteTeam();
const Comp = asChild ? Slot : 'p';
const numberOfRedemptionsLeft = Number(purchase?.bulkCoupon?.maxUses) -
Number(purchase?.bulkCoupon?.usedCount);
return (_jsxs(Comp, { className: cn('', className), children: ["You have", ' ', _jsxs("span", { children: [numberOfRedemptionsLeft, " team", ' ', pluralize('seat', numberOfRedemptionsLeft), " available."] })] }));
};
const InviteLink = ({ children, asChild, className, ...props }) => {
const Comp = asChild ? Slot : Input;
const { purchase, disabled = false, inviteLink } = useInviteTeam();
return (_jsx(Comp, { className: cn('', className), readOnly: true, disabled: disabled, id: "inviteLink", onClick: (e) => {
if (disabled)
return;
e.currentTarget.select();
}, value: disabled ? 'Buy more seats' : inviteLink, ...props }));
};
const CopyInviteLinkButton = ({ children = _jsx(Clipboard, { className: "h-4 w-4", "aria-label": "Copy to clipboard" }), asChild, className, ...props }) => {
const Comp = asChild ? Slot : Button;
const { purchase, disabled = false, inviteLink } = useInviteTeam();
const [_, setCopied] = useCopyToClipboard();
const { toast } = useToast();
return (_jsx(Comp, { variant: "outline", size: "icon", type: "button", "aria-label": "Copy to clipboard", disabled: disabled, onClick: () => {
setCopied(inviteLink);
toast({ title: 'Link copied to clipboard' });
}, ...props, children: children }));
};
const SelfRedeemButton = ({ children = 'Claim 1 seat for yourself', asChild, className, ...props }) => {
const Comp = asChild ? Slot : Button;
const { purchase, disabled = false, userEmail, existingPurchase, } = useInviteTeam();
const [isLoading, setIsLoading] = React.useState(false);
const { toast } = useToast();
const [errorMessage, setErrorMessage] = React.useState(null);
const canRedeem = !existingPurchase;
return canRedeem ? (_jsxs(_Fragment, { children: [_jsx(Comp, { variant: "outline", className: cn('text-primary w-full', className), type: "button", disabled: isLoading || disabled || !userEmail || !canRedeem, onClick: () => {
if (userEmail) {
setIsLoading(true);
handleSelfRedeem(userEmail, purchase.bulkCoupon?.id, purchase.productId, (params) => {
if (params.status === 'success') {
console.log('redeemedPurchase', params.redeemedPurchase);
toast({
title: 'Success! You have successfully redeemed a seat for yourself.',
});
setIsLoading(false);
}
else {
setIsLoading(false);
// TODO: report to sentry or support?
console.debug(params.error);
if (params.error.startsWith('already-purchased-')) {
const message = 'You have already redeemed a seat for yourself. Please contact support if you are having trouble accessing it.';
setErrorMessage(message);
toast({
title: message,
});
}
else {
const message = 'We were unable to redeem a seat for this account. If the issue persists, please reach out to support.';
setErrorMessage(message);
toast({
title: 'We were unable to redeem a seat for this account. If the issue persists, please reach out to support.',
});
}
}
});
}
}, ...props, children: isLoading ? 'Claiming a seat...' : children }), errorMessage && _jsx(Alert, { children: errorMessage })] })) : null;
};
export { Root, SeatsAvailable, InviteLink, CopyInviteLinkButton, SelfRedeemButton, };