Comments, appointments adjustments, fixed some issues

This commit is contained in:
augmentedpotato
2026-04-20 19:19:30 -06:00
parent d3b9c51952
commit 2cb0a94bbb
34 changed files with 402 additions and 104 deletions

View File

@@ -13,8 +13,10 @@ import {
} from "@stripe/react-stripe-js";
import { apiCompleteCheckout } from "@/lib/cartApi";
//Initializes Stripe with the publishable key
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY || "");
//Stripe payment form shown after the user clicks checkout
function PaymentForm({ clientSecret, totalAmount, onSuccess, onCancel }) {
const stripe = useStripe();
const elements = useElements();
@@ -22,6 +24,7 @@ function PaymentForm({ clientSecret, totalAmount, onSuccess, onCancel }) {
const [paying, setPaying] = useState(false);
const [payError, setPayError] = useState(null);
//Confirms the payment with Stripe, then tells the backend to complete the order
async function handlePay(e) {
e.preventDefault();
if (!stripe || !elements) return;
@@ -57,9 +60,6 @@ function PaymentForm({ clientSecret, totalAmount, onSuccess, onCancel }) {
<p className="text-[0.95rem] text-[#555] m-0">
Total to pay: <strong>${parseFloat(totalAmount).toFixed(2)}</strong>
</p>
<div className="bg-[#fffbeb] border border-[#fde68a] rounded-lg px-4 py-3 text-[0.82rem] text-[#854d0e] flex flex-col gap-1">
<div><strong>Demo mode</strong> no real charge. Use test card: <span className="font-mono font-bold">4242 4242 4242 4242</span> · any future date · any 3-digit CVC</div>
</div>
<PaymentElement />
{payError && <p className="bg-[#fff0f0] border border-[#f5c6c6] text-[#c0392b] rounded-lg px-4 py-3 text-[0.9rem] m-0">{payError}</p>}
<div className="flex gap-3 mt-2">
@@ -79,6 +79,7 @@ function PaymentForm({ clientSecret, totalAmount, onSuccess, onCancel }) {
);
}
//Cart page - shows items, coupons, loyalty points, order summary, and checkout
export default function CartPage() {
const { user, loading: authLoading, refreshUser } = useAuth();
const {
@@ -114,12 +115,14 @@ export default function CartPage() {
const [localQuantities, setLocalQuantities] = useState({});
//Redirect unauthenticated users to login
useEffect(() => {
if (!authLoading && !user) {
router.push("/login");
}
}, [authLoading, user, router]);
//Sync local quantity inputs whenever the cart updates from the server
useEffect(() => {
if (cart?.items) {
const map = {};
@@ -129,12 +132,14 @@ export default function CartPage() {
setOptimisticPointsApplied(null);
}, [cart]);
//Cancel any leftover pending checkout if the page loads without a client secret
useEffect(() => {
if (cart?.checkoutPending && !clientSecret) {
cancelCheckout().catch(() => {});
}
}, [cart?.checkoutPending, clientSecret, cancelCheckout]);
//Updates item quantity and rolls back the change if the request fails
async function handleQuantityChange(cartItemId, newQty) {
if (newQty < 1) return;
setLocalQuantities((prev) => ({ ...prev, [cartItemId]: newQty }));
@@ -158,6 +163,7 @@ export default function CartPage() {
catch {}
}
//Applies the typed coupon code and shows the discount type and amount
async function handleApplyCoupon() {
if (!couponInput.trim()) return;
setCouponLoading(true);
@@ -212,6 +218,8 @@ export default function CartPage() {
}
}
//Starts checkout
//Either gets a Stripe client secret for payment or marks the order complete directly
async function handleCheckout() {
if (!cart?.items?.length) return;
setCheckoutLoading(true);
@@ -497,7 +505,7 @@ export default function CartPage() {
)}
{clientSecret && (
<Elements stripe={stripePromise} options={{ clientSecret }}>
<Elements stripe={stripePromise} options={{ clientSecret, wallets: { link: "never" } }}>
<PaymentForm
clientSecret={clientSecret}
totalAmount={checkoutTotal ?? cart.totalAmount}