Add login prompt when non-authenticated users add items to cart
- Create LoginPromptModal component for authentication encouragement - Check customer authentication before adding items to cart - Show modal for non-logged-in users with options to create account, log in, or continue as guest - Allow guests to add items (stored in IndexedDB) while prompting to sign up - Implemented in both StandardProductView (ready-made products) and DesignCanvas (personalized items) - Add server action checkCustomerAuth() for authentication checks Addresses user request: prompt guests to create account or log in when adding items to cart. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 4.5
parent
0cc072fda4
commit
b7379e379b
@@ -6,8 +6,10 @@ import type Konva from 'konva';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import ProductMockup from './ProductMockup';
|
||||
import Price from './Price';
|
||||
import LoginPromptModal from './LoginPromptModal';
|
||||
import { useCart } from '@/lib/cartStore';
|
||||
import { getEffectivePrice } from '@/lib/pricing';
|
||||
import { checkCustomerAuth } from '@/app/actions';
|
||||
import type { DesignElement, PrintArea, ProductDTO } from '@/lib/types';
|
||||
|
||||
const DISPLAY = 560; // css px size the mockup + stage are rendered at
|
||||
@@ -310,6 +312,7 @@ export default function DesignCanvas({ product }: { product: ProductDTO }) {
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||
const [quantity, setQuantity] = useState(1);
|
||||
const [justAdded, setJustAdded] = useState(false);
|
||||
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
||||
const [nodeReadyTick, setNodeReadyTick] = useState(0);
|
||||
|
||||
const stageFrontRef = useRef<Konva.Stage | null>(null);
|
||||
@@ -462,7 +465,12 @@ export default function DesignCanvas({ product }: { product: ProductDTO }) {
|
||||
).price
|
||||
: product.effectivePrice;
|
||||
|
||||
const handleAddToBag = () => {
|
||||
const handleAddToBag = async () => {
|
||||
const { isAuthenticated } = await checkCustomerAuth();
|
||||
if (!isAuthenticated) {
|
||||
setShowLoginPrompt(true);
|
||||
}
|
||||
|
||||
if (!canPersonalize) {
|
||||
const flatPhoto = product.colorPhotos[color] ?? product.imageUrl ?? '';
|
||||
addItem({
|
||||
@@ -861,6 +869,8 @@ export default function DesignCanvas({ product }: { product: ProductDTO }) {
|
||||
</div>
|
||||
{justAdded && <p className="text-sm text-pine">Added to your bag.</p>}
|
||||
</div>
|
||||
|
||||
<LoginPromptModal isOpen={showLoginPrompt} onClose={() => setShowLoginPrompt(false)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
interface LoginPromptModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function LoginPromptModal({ isOpen, onClose }: LoginPromptModalProps) {
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
||||
<div className="mx-4 w-full max-w-sm rounded-lg border border-line bg-paper p-6 shadow-lg">
|
||||
<h2 className="font-display text-2xl">Create an account to shop</h2>
|
||||
<p className="mt-3 text-sm text-muted">
|
||||
Add items to your bag and save your designs. You'll need an account to checkout.
|
||||
</p>
|
||||
|
||||
<div className="mt-6 space-y-3">
|
||||
<Link
|
||||
href="/account/register"
|
||||
onClick={onClose}
|
||||
className="block w-full rounded bg-clay px-4 py-3 text-center text-sm font-medium text-paper transition-colors hover:bg-clay-dark"
|
||||
>
|
||||
Create account
|
||||
</Link>
|
||||
<Link
|
||||
href="/account/login"
|
||||
onClick={onClose}
|
||||
className="block w-full rounded border border-ink px-4 py-3 text-center text-sm font-medium text-ink transition-colors hover:border-clay hover:bg-clay hover:text-paper"
|
||||
>
|
||||
Log in
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="mt-4 w-full text-center text-sm text-muted hover:text-ink"
|
||||
>
|
||||
Continue as guest
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,8 +4,10 @@ import { useState } from 'react';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import ProductMockup from './ProductMockup';
|
||||
import Price from './Price';
|
||||
import LoginPromptModal from './LoginPromptModal';
|
||||
import { useCart } from '@/lib/cartStore';
|
||||
import { getColorName } from '@/lib/colorNames';
|
||||
import { checkCustomerAuth } from '@/app/actions';
|
||||
import type { ProductDTO } from '@/lib/types';
|
||||
|
||||
export default function StandardProductView({ product }: { product: ProductDTO }) {
|
||||
@@ -14,6 +16,7 @@ export default function StandardProductView({ product }: { product: ProductDTO }
|
||||
const [view, setView] = useState<'front' | 'back'>('front');
|
||||
const [quantity, setQuantity] = useState(1);
|
||||
const [justAdded, setJustAdded] = useState(false);
|
||||
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
||||
const addItem = useCart((s) => s.addItem);
|
||||
|
||||
const hasBack = Boolean(product.imageUrlBack);
|
||||
@@ -44,7 +47,12 @@ export default function StandardProductView({ product }: { product: ProductDTO }
|
||||
return <img src={activePhoto} alt={product.name} className="h-full w-full object-cover" />;
|
||||
}
|
||||
|
||||
const handleAddToBag = () => {
|
||||
const handleAddToBag = async () => {
|
||||
const { isAuthenticated } = await checkCustomerAuth();
|
||||
if (!isAuthenticated) {
|
||||
setShowLoginPrompt(true);
|
||||
}
|
||||
|
||||
addItem({
|
||||
cartItemId: uuid(),
|
||||
productId: product.id,
|
||||
@@ -182,6 +190,8 @@ export default function StandardProductView({ product }: { product: ProductDTO }
|
||||
</div>
|
||||
{justAdded && <p className="text-sm text-pine">Added to your bag.</p>}
|
||||
</div>
|
||||
|
||||
<LoginPromptModal isOpen={showLoginPrompt} onClose={() => setShowLoginPrompt(false)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user