mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-12 17:51:53 +08:00
* Course landing page * Add ai course page * wip * wip * wip * wip * wip * wip * wip * wip: error handling * wip * wip * wip * wip: ai course progress * wip * wip * wip * feat: code highlighting * feat: usage limit * feat: follow up message * Update UI * wip * Add course content * wip: autogrow textarea & examples * Update types * Update * fix: add highlight to the AI chat * UI changes * Refactor * Update * Improve outline style * Improve spacing * Improve spacing * UI changes for sidebar * Update UI for sidebar * Improve course UI * Mark done, undone * Add toggle lesson done/undone * Update forward backward UI * wip * Minor ui change * Responsiveness of sidebar * wip * wip * wip: billing page * wip * Update UI * fix: hide upgrade if paid user * feat: token usage * feat: list ai courses * fix: limit for followup * Course content responsiveness * Make course content responsive * Responsiveness * Outline button * Responsiveness of course content * Responsiveness of course content * Add course upgrade button * Update design for upgrade * Improve logic for upgrade and limits button * Limits and errors * Add lesson count * Add course card * Improve UI for course generator * Update course functionality * Refactor AI course generation * Responsiveness of screen * Improve * Add responsiveness * Improve empty billing page design * Add empty billing screen * Update UI for billing page * Update UI for billing page * Update UI for billing page * Update billing page design * Update * Remove sidebar * Update --------- Co-authored-by: Arik Chakma <arikchangma@gmail.com>
97 lines
2.7 KiB
TypeScript
97 lines
2.7 KiB
TypeScript
import { useMutation } from '@tanstack/react-query';
|
|
import type { USER_SUBSCRIPTION_PLAN_PRICES } from '../../queries/billing';
|
|
import { Modal } from '../Modal';
|
|
import { queryClient } from '../../stores/query-client';
|
|
import { useToast } from '../../hooks/use-toast';
|
|
import { VerifyUpgrade } from './VerifyUpgrade';
|
|
import { Loader2Icon } from 'lucide-react';
|
|
import { httpPost } from '../../lib/query-http';
|
|
|
|
type UpdatePlanBody = {
|
|
priceId: string;
|
|
};
|
|
|
|
type UpdatePlanResponse = {
|
|
status: 'ok';
|
|
};
|
|
|
|
type UpdatePlanConfirmationProps = {
|
|
planDetails: (typeof USER_SUBSCRIPTION_PLAN_PRICES)[number];
|
|
onClose: () => void;
|
|
onCancel: () => void;
|
|
};
|
|
|
|
export function UpdatePlanConfirmation(props: UpdatePlanConfirmationProps) {
|
|
const { planDetails, onClose, onCancel } = props;
|
|
|
|
const toast = useToast();
|
|
const {
|
|
mutate: updatePlan,
|
|
isPending,
|
|
status,
|
|
} = useMutation(
|
|
{
|
|
mutationFn: (body: UpdatePlanBody) => {
|
|
return httpPost<UpdatePlanResponse>(
|
|
'/v1-update-subscription-plan',
|
|
body,
|
|
);
|
|
},
|
|
onError: (error) => {
|
|
console.error(error);
|
|
toast.error(error?.message || 'Failed to Create Customer Portal');
|
|
},
|
|
},
|
|
queryClient,
|
|
);
|
|
|
|
if (!planDetails) {
|
|
return null;
|
|
}
|
|
|
|
const selectedPrice = planDetails;
|
|
if (status === 'success') {
|
|
return <VerifyUpgrade newPriceId={selectedPrice.priceId} />;
|
|
}
|
|
|
|
return (
|
|
<Modal
|
|
onClose={isPending ? () => {} : onClose}
|
|
bodyClassName="rounded-xl bg-white p-6"
|
|
>
|
|
<h3 className="text-xl font-bold text-black">Subscription Update</h3>
|
|
<p className="mt-2 text-balance text-gray-600">
|
|
Your plan will be updated to the{' '}
|
|
<b className="text-black">{planDetails.interval}</b> plan, and will
|
|
be charged{' '}
|
|
<b className="text-black">
|
|
${selectedPrice.amount}/{selectedPrice.interval}
|
|
</b>
|
|
.
|
|
</p>
|
|
|
|
<div className="mt-6 grid grid-cols-2 gap-3">
|
|
<button
|
|
className="rounded-md border border-gray-200 py-2 text-sm font-semibold hover:bg-gray-50 transition-colors disabled:opacity-50"
|
|
onClick={onCancel}
|
|
disabled={isPending}
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
className="flex items-center justify-center rounded-md bg-purple-600 py-2 text-sm font-semibold text-white hover:bg-purple-500 transition-colors disabled:opacity-50"
|
|
disabled={isPending}
|
|
onClick={() => {
|
|
updatePlan({ priceId: selectedPrice.priceId });
|
|
}}
|
|
>
|
|
{isPending && (
|
|
<Loader2Icon className="size-4 animate-spin stroke-[2.5] mr-2" />
|
|
)}
|
|
{!isPending && 'Confirm'}
|
|
</button>
|
|
</div>
|
|
</Modal>
|
|
);
|
|
}
|