Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
transpilePackages: ["@spree/next", "@spree/sdk"],
images: {
qualities: [25, 50, 75, 100],
dangerouslyAllowLocalIP: true, // Allow localhost images in development
Expand Down
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"check": "biome check ."
},
"dependencies": {
"@spree/next": "^0.1.1",
Comment thread
damianlegawiec marked this conversation as resolved.
"@spree/sdk": "^0.1.4",
"next": "^16",
"react": "^19",
Expand Down
5 changes: 2 additions & 3 deletions src/app/[country]/[locale]/(checkout)/checkout/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,13 @@ function CheckoutSidebar({
}

export default function CheckoutPage({ params }: CheckoutPageProps) {
// use() must be called before all other hooks to avoid hook order issues
const { id: orderId } = use(params);
const router = useRouter();
const pathname = usePathname();
const basePath = extractBasePath(pathname);
const { setSummaryContent } = useCheckout();

// Use React 19's use() hook to unwrap the params Promise
const { id: orderId } = use(params);

const [order, setOrder] = useState<StoreOrder | null>(null);
const [shipments, setShipments] = useState<StoreShipment[]>([]);
const [countries, setCountries] = useState<StoreCountry[]>([]);
Expand Down
166 changes: 85 additions & 81 deletions src/components/checkout/AddressStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -274,92 +274,96 @@ export function AddressStep({
};

return (
<form onSubmit={handleSubmit} className="space-y-8">
{/* Sign-in prompt for guests */}
{!isAuthenticated && (
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<p className="text-sm text-blue-800">
Already have an account?{" "}
<Link
href={signInUrl}
className="font-medium text-blue-600 hover:text-blue-700 underline"
<>
<form onSubmit={handleSubmit} className="space-y-8">
{/* Sign-in prompt for guests */}
{!isAuthenticated && (
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<p className="text-sm text-blue-800">
Already have an account?{" "}
<Link
href={signInUrl}
className="font-medium text-blue-600 hover:text-blue-700 underline"
>
Sign in
</Link>{" "}
to access your saved addresses and order history.
</p>
</div>
)}

{/* Contact Information */}
<div className="bg-white rounded-lg border border-gray-200 p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-4">
Contact Information
</h2>
<div>
<label
htmlFor="email"
className="block text-sm font-medium text-gray-700"
>
Sign in
</Link>{" "}
to access your saved addresses and order history.
</p>
Email address
</label>
<input
type="email"
id="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
disabled={isAuthenticated}
className="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent disabled:bg-gray-50 disabled:text-gray-500"
placeholder="you@example.com"
/>
{isAuthenticated && (
<p className="mt-1 text-xs text-gray-500">
Using your account email address
</p>
)}
</div>
</div>
)}

{/* Contact Information */}
<div className="bg-white rounded-lg border border-gray-200 p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-4">
Contact Information
</h2>
<div>
<label
htmlFor="email"
className="block text-sm font-medium text-gray-700"
>
Email address
</label>
<input
type="email"
id="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
disabled={isAuthenticated}
className="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent disabled:bg-gray-50 disabled:text-gray-500"
placeholder="you@example.com"
/>
{isAuthenticated && (
<p className="mt-1 text-xs text-gray-500">
Using your account email address
</p>
{/* Shipping Address */}
<div className="bg-white rounded-lg border border-gray-200 p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-4">
Shipping Address
</h2>
{isAuthenticated && savedAddresses.length > 0 ? (
<AddressSelector
savedAddresses={savedAddresses}
currentAddress={shipAddress}
countries={countries}
states={shipStates}
loadingStates={isPendingShip}
onChange={updateShipAddress}
onSelectSavedAddress={handleSelectSavedAddress}
onEditAddress={
onUpdateSavedAddress ? handleEditAddress : undefined
}
idPrefix="ship"
/>
) : (
<AddressForm
address={shipAddress}
countries={countries}
states={shipStates}
loadingStates={isPendingShip}
onChange={updateShipAddress}
idPrefix="ship"
/>
)}
</div>
</div>

{/* Shipping Address */}
<div className="bg-white rounded-lg border border-gray-200 p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-4">
Shipping Address
</h2>
{isAuthenticated && savedAddresses.length > 0 ? (
<AddressSelector
savedAddresses={savedAddresses}
currentAddress={shipAddress}
countries={countries}
states={shipStates}
loadingStates={isPendingShip}
onChange={updateShipAddress}
onSelectSavedAddress={handleSelectSavedAddress}
onEditAddress={onUpdateSavedAddress ? handleEditAddress : undefined}
idPrefix="ship"
/>
) : (
<AddressForm
address={shipAddress}
countries={countries}
states={shipStates}
loadingStates={isPendingShip}
onChange={updateShipAddress}
idPrefix="ship"
/>
)}
</div>

{/* Submit */}
<div className="flex justify-end">
<button
type="submit"
disabled={processing}
className="px-6 py-3 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{processing ? "Saving..." : "Continue to Delivery"}
</button>
</div>
{/* Submit */}
<div className="flex justify-end">
<button
type="submit"
disabled={processing}
className="px-6 py-3 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{processing ? "Saving..." : "Continue to Delivery"}
</button>
</div>
</form>

{/* Edit Address Modal */}
{editingAddress && (
Expand Down Expand Up @@ -613,7 +617,7 @@ export function AddressStep({
</div>
</div>
)}
</form>
</>
);
}

Expand Down
41 changes: 22 additions & 19 deletions src/contexts/CartContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@ export function CartProvider({ children }: { children: ReactNode }) {
async (variantId: string, quantity = 1) => {
setUpdating(true);
try {
const updatedCart = await addToCartAction(variantId, quantity);
setCart(updatedCart);
setIsOpen(true); // Open cart drawer after adding
router.refresh();
} catch (error) {
console.error("Failed to add item to cart:", error);
throw error;
const result = await addToCartAction(variantId, quantity);
if (result.success) {
setCart(result.cart);
setIsOpen(true); // Open cart drawer after adding
router.refresh();
} else {
console.error("Failed to add item to cart:", result.error);
}
} finally {
setUpdating(false);
}
Expand All @@ -76,12 +77,13 @@ export function CartProvider({ children }: { children: ReactNode }) {
async (lineItemId: string, quantity: number) => {
setUpdating(true);
try {
const updatedCart = await updateCartItemAction(lineItemId, quantity);
setCart(updatedCart);
router.refresh();
} catch (error) {
console.error("Failed to update cart item:", error);
throw error;
const result = await updateCartItemAction(lineItemId, quantity);
if (result.success) {
setCart(result.cart);
router.refresh();
} else {
console.error("Failed to update cart item:", result.error);
}
} finally {
setUpdating(false);
}
Expand All @@ -93,12 +95,13 @@ export function CartProvider({ children }: { children: ReactNode }) {
async (lineItemId: string) => {
setUpdating(true);
try {
const updatedCart = await removeCartItemAction(lineItemId);
setCart(updatedCart);
router.refresh();
} catch (error) {
console.error("Failed to remove cart item:", error);
throw error;
const result = await removeCartItemAction(lineItemId);
if (result.success) {
setCart(result.cart);
router.refresh();
} else {
console.error("Failed to remove cart item:", result.error);
}
} finally {
setUpdating(false);
}
Expand Down
Loading