diff --git a/.changeset/cart-components-mutations.md b/.changeset/cart-components-mutations.md
new file mode 100644
index 00000000..74b6a5cf
--- /dev/null
+++ b/.changeset/cart-components-mutations.md
@@ -0,0 +1,7 @@
+---
+"@godaddy/react": patch
+---
+
+Added cart functionality with `useAddToCart` hook and `Cart` component.
+Fixed hydration mismatch in `Cart` component.
+Removed internal environment references.
diff --git a/examples/nextjs/app/providers.tsx b/examples/nextjs/app/providers.tsx
index 71f3d85a..d96f8d77 100644
--- a/examples/nextjs/app/providers.tsx
+++ b/examples/nextjs/app/providers.tsx
@@ -25,6 +25,7 @@ export function Providers({ children }: { children: React.ReactNode }) {
apiHost={process.env.NEXT_PUBLIC_GODADDY_API_HOST}
storeId={process.env.NEXT_PUBLIC_GODADDY_STORE_ID}
clientId={process.env.NEXT_PUBLIC_GODADDY_CLIENT_ID}
+ channelId={process.env.NEXT_PUBLIC_GODADDY_CHANNEL_ID}
Link={Link}
appearance={{
variables: { primary: '#ff0000', 'primary-foreground': '#FFFFFF' },
diff --git a/examples/nextjs/app/store/actions.ts b/examples/nextjs/app/store/actions.ts
new file mode 100644
index 00000000..1cc06f39
--- /dev/null
+++ b/examples/nextjs/app/store/actions.ts
@@ -0,0 +1,87 @@
+'use server';
+
+import { createCheckoutSession } from '@godaddy/react/server';
+import { redirect } from 'next/navigation';
+
+export async function checkoutWithOrder(orderId: string) {
+ const session = await createCheckoutSession(
+ {
+ returnUrl: `https://godaddy.com`,
+ successUrl: `https://godaddy.com/success`,
+ draftOrderId: orderId,
+ storeId: process.env.NEXT_PUBLIC_GODADDY_STORE_ID || '',
+ channelId: process.env.NEXT_PUBLIC_GODADDY_CHANNEL_ID || '',
+ enableShippingAddressCollection: true,
+ enableBillingAddressCollection: true,
+ enableTaxCollection: true,
+ shipping: {
+ fulfillmentLocationId: 'default-location',
+ originAddress: {
+ addressLine1: '1600 Pennsylvania Ave NW',
+ adminArea1: 'DC',
+ adminArea3: 'Washington',
+ countryCode: 'US',
+ postalCode: '20500',
+ },
+ },
+ taxes: {
+ originAddress: {
+ addressLine1: '1600 Pennsylvania Ave NW',
+ adminArea1: 'DC',
+ adminArea3: 'Washington',
+ countryCode: 'US',
+ postalCode: '20500',
+ },
+ },
+ locations: [
+ {
+ id: 'default-location',
+ isDefault: true,
+ address: {
+ addressLine1: '1600 Pennsylvania Ave NW',
+ adminArea1: 'DC',
+ adminArea3: 'Washington',
+ countryCode: 'US',
+ postalCode: '20500',
+ },
+ },
+ ],
+ paymentMethods: {
+ card: {
+ processor: 'godaddy',
+ checkoutTypes: ['standard'],
+ },
+ express: {
+ processor: 'godaddy',
+ checkoutTypes: ['express'],
+ },
+ paypal: {
+ processor: 'paypal',
+ checkoutTypes: ['standard'],
+ },
+ offline: {
+ processor: 'offline',
+ checkoutTypes: ['standard'],
+ },
+ },
+ },
+ {
+ auth: {
+ clientId: process.env.NEXT_PUBLIC_GODADDY_CLIENT_ID || '',
+ clientSecret: process.env.GODADDY_CLIENT_SECRET || '',
+ },
+ }
+ );
+
+ if (!session) {
+ throw new Error('Failed to create checkout session');
+ }
+
+ console.log({ session });
+
+ if (!session.url) {
+ throw new Error('No checkout URL returned');
+ }
+
+ redirect(session.url);
+}
diff --git a/examples/nextjs/app/store/layout.tsx b/examples/nextjs/app/store/layout.tsx
index 9b003c0e..a638c781 100644
--- a/examples/nextjs/app/store/layout.tsx
+++ b/examples/nextjs/app/store/layout.tsx
@@ -3,6 +3,7 @@
import { Cart } from '@godaddy/react';
import { ShoppingCart } from 'lucide-react';
import { createContext, useContext, useState } from 'react';
+import { checkoutWithOrder } from './actions';
interface CartContextType {
openCart: () => void;
@@ -25,10 +26,20 @@ export default function StoreLayout({
children: React.ReactNode;
}) {
const [isCartOpen, setIsCartOpen] = useState(false);
+ const [isCheckingOut, setIsCheckingOut] = useState(false);
const openCart = () => setIsCartOpen(true);
const closeCart = () => setIsCartOpen(false);
+ const handleCheckout = async (orderId: string) => {
+ setIsCheckingOut(true);
+ try {
+ await checkoutWithOrder(orderId);
+ } catch (_error) {
+ setIsCheckingOut(false);
+ }
+ };
+
return (
@@ -43,7 +54,12 @@ export default function StoreLayout({
{children}
-
+
);
diff --git a/examples/nextjs/app/store/product/[productId]/product.tsx b/examples/nextjs/app/store/product/[productId]/product.tsx
index bce327b8..9aa51c71 100644
--- a/examples/nextjs/app/store/product/[productId]/product.tsx
+++ b/examples/nextjs/app/store/product/[productId]/product.tsx
@@ -3,8 +3,11 @@
import { ProductDetails } from '@godaddy/react';
import { ArrowLeft } from 'lucide-react';
import Link from 'next/link';
+import { useCart } from '../../layout';
export default function Product({ productId }: { productId: string }) {
+ const { openCart } = useCart();
+
return (
);
}
diff --git a/examples/nextjs/app/store/products.tsx b/examples/nextjs/app/store/products.tsx
index 324a4b06..48a01459 100644
--- a/examples/nextjs/app/store/products.tsx
+++ b/examples/nextjs/app/store/products.tsx
@@ -1,11 +1,17 @@
'use client';
import { ProductGrid } from '@godaddy/react';
+import { useCart } from './layout';
export default function ProductsPage() {
+ const { openCart } = useCart();
+
return (
-
`/store/product/${sku}`} />
+ `/store/product/${sku}`}
+ onAddToCartSuccess={openCart}
+ />
);
}
diff --git a/packages/localizations/src/deDe.ts b/packages/localizations/src/deDe.ts
index 139c2acf..8f884e1c 100644
--- a/packages/localizations/src/deDe.ts
+++ b/packages/localizations/src/deDe.ts
@@ -348,4 +348,39 @@ export const deDe = {
DEPENDENCY_ERROR:
'Wir können Ihre Bestellung derzeit nicht bearbeiten. Bitte warten Sie einen Moment und versuchen Sie es erneut',
},
+ storefront: {
+ product: 'Produkt',
+ sale: 'SALE',
+ noImage: 'Kein Bild',
+ noImageAvailable: 'Kein Bild verfügbar',
+ selectOptions: 'Optionen auswählen',
+ adding: 'Wird hinzugefügt...',
+ addToCart: 'In den Warenkorb',
+ shoppingCart: 'Warenkorb',
+ failedToLoadCart: 'Warenkorb konnte nicht geladen werden:',
+ retry: 'Wiederholen',
+ yourCartIsEmpty: 'Ihr Warenkorb ist leer',
+ addItemsToGetStarted: 'Fügen Sie Artikel hinzu, um zu beginnen',
+ errorLoadingProducts: 'Fehler beim Laden der Produkte:',
+ errorLoadingProduct: 'Fehler beim Laden des Produkts:',
+ productNotFound: 'Produkt nicht gefunden',
+ loadingVariantDetails: 'Variantendetails werden geladen...',
+ combinationNotAvailable:
+ 'Diese Kombination ist nicht verfügbar. Bitte wählen Sie andere Optionen.',
+ variantsMatch:
+ 'Varianten entsprechen Ihrer Auswahl. Wählen Sie weitere Attribute aus, um die Auswahl einzugrenzen.',
+ quantity: 'Menge',
+ addingToCart: 'Wird zum Warenkorb hinzugefügt...',
+ outOfStock: 'Nicht vorrätig',
+ viewDetails: 'Details anzeigen',
+ productType: 'Produkttyp:',
+ productId: 'Produkt-ID:',
+ selectedSku: 'Ausgewählte SKU:',
+ stockStatus: 'Lagerstatus:',
+ lowStock: 'Geringer Lagerbestand',
+ inStock: 'Auf Lager',
+ remove: 'Entfernen',
+ removing: 'Wird entfernt...',
+ checkout: 'Zur Kasse',
+ },
};
diff --git a/packages/localizations/src/enIe.ts b/packages/localizations/src/enIe.ts
index 04f21481..ec6a20ae 100644
--- a/packages/localizations/src/enIe.ts
+++ b/packages/localizations/src/enIe.ts
@@ -325,4 +325,39 @@ export const enIe = {
DEPENDENCY_ERROR:
"We're unable to process your order right now. Please wait a moment and try again",
},
+ storefront: {
+ product: 'Product',
+ sale: 'SALE',
+ noImage: 'No image',
+ noImageAvailable: 'No image available',
+ selectOptions: 'Select Options',
+ adding: 'Adding...',
+ addToCart: 'Add to Cart',
+ shoppingCart: 'Shopping Cart',
+ failedToLoadCart: 'Failed to load cart:',
+ retry: 'Retry',
+ yourCartIsEmpty: 'Your cart is empty',
+ addItemsToGetStarted: 'Add items to get started',
+ errorLoadingProducts: 'Error loading products:',
+ errorLoadingProduct: 'Error loading product:',
+ productNotFound: 'Product not found',
+ loadingVariantDetails: 'Loading variant details...',
+ combinationNotAvailable:
+ 'This combination is not available. Please select different options.',
+ variantsMatch:
+ 'variants match your selection. Select more attributes to narrow down.',
+ quantity: 'Quantity',
+ addingToCart: 'Adding to Cart...',
+ outOfStock: 'Out of Stock',
+ viewDetails: 'View Details',
+ productType: 'Product Type:',
+ productId: 'Product ID:',
+ selectedSku: 'Selected SKU:',
+ stockStatus: 'Stock Status:',
+ lowStock: 'Low Stock',
+ inStock: 'In Stock',
+ remove: 'Remove',
+ removing: 'Removing...',
+ checkout: 'Checkout',
+ },
};
diff --git a/packages/localizations/src/enUs.ts b/packages/localizations/src/enUs.ts
index 89d960a6..80fc5793 100644
--- a/packages/localizations/src/enUs.ts
+++ b/packages/localizations/src/enUs.ts
@@ -325,4 +325,39 @@ export const enUs = {
DEPENDENCY_ERROR:
"We're unable to process your order right now. Please wait a moment and try again",
},
+ storefront: {
+ product: 'Product',
+ sale: 'SALE',
+ noImage: 'No image',
+ noImageAvailable: 'No image available',
+ selectOptions: 'Select Options',
+ adding: 'Adding...',
+ addToCart: 'Add to Cart',
+ shoppingCart: 'Shopping Cart',
+ failedToLoadCart: 'Failed to load cart:',
+ retry: 'Retry',
+ yourCartIsEmpty: 'Your cart is empty',
+ addItemsToGetStarted: 'Add items to get started',
+ errorLoadingProducts: 'Error loading products:',
+ errorLoadingProduct: 'Error loading product:',
+ productNotFound: 'Product not found',
+ loadingVariantDetails: 'Loading variant details...',
+ combinationNotAvailable:
+ 'This combination is not available. Please select different options.',
+ variantsMatch:
+ 'variants match your selection. Select more attributes to narrow down.',
+ quantity: 'Quantity',
+ addingToCart: 'Adding to Cart...',
+ outOfStock: 'Out of Stock',
+ viewDetails: 'View Details',
+ productType: 'Product Type:',
+ productId: 'Product ID:',
+ selectedSku: 'Selected SKU:',
+ stockStatus: 'Stock Status:',
+ lowStock: 'Low Stock',
+ inStock: 'In Stock',
+ remove: 'Remove',
+ removing: 'Removing...',
+ checkout: 'Checkout',
+ },
};
diff --git a/packages/localizations/src/esAr.ts b/packages/localizations/src/esAr.ts
index e6f6a692..eb0df26c 100644
--- a/packages/localizations/src/esAr.ts
+++ b/packages/localizations/src/esAr.ts
@@ -331,4 +331,39 @@ export const esAr = {
DEPENDENCY_ERROR:
'No podemos procesar su pedido en este momento. Espere un momento e inténtelo de nuevo',
},
+ storefront: {
+ product: 'Producto',
+ sale: 'OFERTA',
+ noImage: 'Sin imagen',
+ noImageAvailable: 'Sin imagen disponible',
+ selectOptions: 'Seleccionar opciones',
+ adding: 'Agregando...',
+ addToCart: 'Agregar al carrito',
+ shoppingCart: 'Carrito de compras',
+ failedToLoadCart: 'Error al cargar el carrito:',
+ retry: 'Reintentar',
+ yourCartIsEmpty: 'Tu carrito está vacío',
+ addItemsToGetStarted: 'Agregá artículos para comenzar',
+ errorLoadingProducts: 'Error al cargar productos:',
+ errorLoadingProduct: 'Error al cargar producto:',
+ productNotFound: 'Producto no encontrado',
+ loadingVariantDetails: 'Cargando detalles de la variante...',
+ combinationNotAvailable:
+ 'Esta combinación no está disponible. Por favor seleccioná opciones diferentes.',
+ variantsMatch:
+ 'variantes coinciden con tu selección. Seleccioná más atributos para reducir las opciones.',
+ quantity: 'Cantidad',
+ addingToCart: 'Agregando al carrito...',
+ outOfStock: 'Agotado',
+ viewDetails: 'Ver detalles',
+ productType: 'Tipo de producto:',
+ productId: 'ID del producto:',
+ selectedSku: 'SKU seleccionado:',
+ stockStatus: 'Estado del stock:',
+ lowStock: 'Stock bajo',
+ inStock: 'En stock',
+ remove: 'Eliminar',
+ removing: 'Eliminando...',
+ checkout: 'Pagar',
+ },
};
diff --git a/packages/localizations/src/esCl.ts b/packages/localizations/src/esCl.ts
index ba005740..c09cf98f 100644
--- a/packages/localizations/src/esCl.ts
+++ b/packages/localizations/src/esCl.ts
@@ -333,4 +333,39 @@ export const esCl = {
DEPENDENCY_ERROR:
'No podemos procesar su pedido en este momento. Espere un momento e inténtelo de nuevo',
},
+ storefront: {
+ product: 'Producto',
+ sale: 'OFERTA',
+ noImage: 'Sin imagen',
+ noImageAvailable: 'Sin imagen disponible',
+ selectOptions: 'Seleccionar opciones',
+ adding: 'Agregando...',
+ addToCart: 'Agregar al carrito',
+ shoppingCart: 'Carrito de compras',
+ failedToLoadCart: 'Error al cargar el carrito:',
+ retry: 'Reintentar',
+ yourCartIsEmpty: 'Tu carrito está vacío',
+ addItemsToGetStarted: 'Agrega artículos para comenzar',
+ errorLoadingProducts: 'Error al cargar productos:',
+ errorLoadingProduct: 'Error al cargar producto:',
+ productNotFound: 'Producto no encontrado',
+ loadingVariantDetails: 'Cargando detalles de la variante...',
+ combinationNotAvailable:
+ 'Esta combinación no está disponible. Por favor selecciona opciones diferentes.',
+ variantsMatch:
+ 'variantes coinciden con tu selección. Selecciona más atributos para reducir las opciones.',
+ quantity: 'Cantidad',
+ addingToCart: 'Agregando al carrito...',
+ outOfStock: 'Agotado',
+ viewDetails: 'Ver detalles',
+ productType: 'Tipo de producto:',
+ productId: 'ID del producto:',
+ selectedSku: 'SKU seleccionado:',
+ stockStatus: 'Estado del stock:',
+ lowStock: 'Stock bajo',
+ inStock: 'En stock',
+ remove: 'Eliminar',
+ removing: 'Eliminando...',
+ checkout: 'Pagar',
+ },
};
diff --git a/packages/localizations/src/esCo.ts b/packages/localizations/src/esCo.ts
index 0833baae..5ee93d15 100644
--- a/packages/localizations/src/esCo.ts
+++ b/packages/localizations/src/esCo.ts
@@ -331,4 +331,39 @@ export const esCo = {
DEPENDENCY_ERROR:
'No podemos procesar su pedido en este momento. Espere un momento e inténtelo de nuevo',
},
+ storefront: {
+ product: 'Producto',
+ sale: 'OFERTA',
+ noImage: 'Sin imagen',
+ noImageAvailable: 'Sin imagen disponible',
+ selectOptions: 'Seleccionar opciones',
+ adding: 'Agregando...',
+ addToCart: 'Agregar al carrito',
+ shoppingCart: 'Carrito de compras',
+ failedToLoadCart: 'Error al cargar el carrito:',
+ retry: 'Reintentar',
+ yourCartIsEmpty: 'Tu carrito está vacío',
+ addItemsToGetStarted: 'Agrega artículos para comenzar',
+ errorLoadingProducts: 'Error al cargar productos:',
+ errorLoadingProduct: 'Error al cargar producto:',
+ productNotFound: 'Producto no encontrado',
+ loadingVariantDetails: 'Cargando detalles de la variante...',
+ combinationNotAvailable:
+ 'Esta combinación no está disponible. Por favor selecciona opciones diferentes.',
+ variantsMatch:
+ 'variantes coinciden con tu selección. Selecciona más atributos para reducir las opciones.',
+ quantity: 'Cantidad',
+ addingToCart: 'Agregando al carrito...',
+ outOfStock: 'Agotado',
+ viewDetails: 'Ver detalles',
+ productType: 'Tipo de producto:',
+ productId: 'ID del producto:',
+ selectedSku: 'SKU seleccionado:',
+ stockStatus: 'Estado del stock:',
+ lowStock: 'Stock bajo',
+ inStock: 'En stock',
+ remove: 'Eliminar',
+ removing: 'Eliminando...',
+ checkout: 'Pagar',
+ },
};
diff --git a/packages/localizations/src/esEs.ts b/packages/localizations/src/esEs.ts
index 349a43fe..5ce4fd31 100644
--- a/packages/localizations/src/esEs.ts
+++ b/packages/localizations/src/esEs.ts
@@ -336,4 +336,39 @@ export const esEs = {
DEPENDENCY_ERROR:
'No podemos procesar su pedido en este momento. Espere un momento e inténtelo de nuevo',
},
+ storefront: {
+ product: 'Producto',
+ sale: 'OFERTA',
+ noImage: 'Sin imagen',
+ noImageAvailable: 'Sin imagen disponible',
+ selectOptions: 'Seleccionar opciones',
+ adding: 'Añadiendo...',
+ addToCart: 'Añadir al carrito',
+ shoppingCart: 'Carrito de compras',
+ failedToLoadCart: 'Error al cargar el carrito:',
+ retry: 'Reintentar',
+ yourCartIsEmpty: 'Tu carrito está vacío',
+ addItemsToGetStarted: 'Añade artículos para comenzar',
+ errorLoadingProducts: 'Error al cargar productos:',
+ errorLoadingProduct: 'Error al cargar producto:',
+ productNotFound: 'Producto no encontrado',
+ loadingVariantDetails: 'Cargando detalles de la variante...',
+ combinationNotAvailable:
+ 'Esta combinación no está disponible. Por favor seleccione opciones diferentes.',
+ variantsMatch:
+ 'variantes coinciden con tu selección. Selecciona más atributos para reducir las opciones.',
+ quantity: 'Cantidad',
+ addingToCart: 'Añadiendo al carrito...',
+ outOfStock: 'Agotado',
+ viewDetails: 'Ver detalles',
+ productType: 'Tipo de producto:',
+ productId: 'ID del producto:',
+ selectedSku: 'SKU seleccionado:',
+ stockStatus: 'Estado del stock:',
+ lowStock: 'Stock bajo',
+ inStock: 'En stock',
+ remove: 'Eliminar',
+ removing: 'Eliminando...',
+ checkout: 'Pagar',
+ },
};
diff --git a/packages/localizations/src/esMx.ts b/packages/localizations/src/esMx.ts
index e7bd89a1..cefaa7b1 100644
--- a/packages/localizations/src/esMx.ts
+++ b/packages/localizations/src/esMx.ts
@@ -332,4 +332,39 @@ export const esMx = {
DEPENDENCY_ERROR:
'No podemos procesar su pedido en este momento. Espere un momento e inténtelo de nuevo',
},
+ storefront: {
+ product: 'Producto',
+ sale: 'OFERTA',
+ noImage: 'Sin imagen',
+ noImageAvailable: 'Sin imagen disponible',
+ selectOptions: 'Seleccionar opciones',
+ adding: 'Agregando...',
+ addToCart: 'Agregar al carrito',
+ shoppingCart: 'Carrito de compras',
+ failedToLoadCart: 'Error al cargar el carrito:',
+ retry: 'Reintentar',
+ yourCartIsEmpty: 'Tu carrito está vacío',
+ addItemsToGetStarted: 'Agrega artículos para comenzar',
+ errorLoadingProducts: 'Error al cargar productos:',
+ errorLoadingProduct: 'Error al cargar producto:',
+ productNotFound: 'Producto no encontrado',
+ loadingVariantDetails: 'Cargando detalles de la variante...',
+ combinationNotAvailable:
+ 'Esta combinación no está disponible. Por favor selecciona opciones diferentes.',
+ variantsMatch:
+ 'variantes coinciden con tu selección. Selecciona más atributos para reducir las opciones.',
+ quantity: 'Cantidad',
+ addingToCart: 'Agregando al carrito...',
+ outOfStock: 'Agotado',
+ viewDetails: 'Ver detalles',
+ productType: 'Tipo de producto:',
+ productId: 'ID del producto:',
+ selectedSku: 'SKU seleccionado:',
+ stockStatus: 'Estado del stock:',
+ lowStock: 'Stock bajo',
+ inStock: 'En stock',
+ remove: 'Eliminar',
+ removing: 'Eliminando...',
+ checkout: 'Pagar',
+ },
};
diff --git a/packages/localizations/src/esPe.ts b/packages/localizations/src/esPe.ts
index ef3876e2..b2c01a8d 100644
--- a/packages/localizations/src/esPe.ts
+++ b/packages/localizations/src/esPe.ts
@@ -331,4 +331,39 @@ export const esPe = {
DEPENDENCY_ERROR:
'No podemos procesar su pedido en este momento. Espere un momento e inténtelo de nuevo',
},
+ storefront: {
+ product: 'Producto',
+ sale: 'OFERTA',
+ noImage: 'Sin imagen',
+ noImageAvailable: 'Sin imagen disponible',
+ selectOptions: 'Seleccionar opciones',
+ adding: 'Agregando...',
+ addToCart: 'Agregar al carrito',
+ shoppingCart: 'Carrito de compras',
+ failedToLoadCart: 'Error al cargar el carrito:',
+ retry: 'Reintentar',
+ yourCartIsEmpty: 'Tu carrito está vacío',
+ addItemsToGetStarted: 'Agrega artículos para comenzar',
+ errorLoadingProducts: 'Error al cargar productos:',
+ errorLoadingProduct: 'Error al cargar producto:',
+ productNotFound: 'Producto no encontrado',
+ loadingVariantDetails: 'Cargando detalles de la variante...',
+ combinationNotAvailable:
+ 'Esta combinación no está disponible. Por favor selecciona opciones diferentes.',
+ variantsMatch:
+ 'variantes coinciden con tu selección. Selecciona más atributos para reducir las opciones.',
+ quantity: 'Cantidad',
+ addingToCart: 'Agregando al carrito...',
+ outOfStock: 'Agotado',
+ viewDetails: 'Ver detalles',
+ productType: 'Tipo de producto:',
+ productId: 'ID del producto:',
+ selectedSku: 'SKU seleccionado:',
+ stockStatus: 'Estado del stock:',
+ lowStock: 'Stock bajo',
+ inStock: 'En stock',
+ remove: 'Eliminar',
+ removing: 'Eliminando...',
+ checkout: 'Pagar',
+ },
};
diff --git a/packages/localizations/src/esUs.ts b/packages/localizations/src/esUs.ts
index 89bbf0fb..55fd477a 100644
--- a/packages/localizations/src/esUs.ts
+++ b/packages/localizations/src/esUs.ts
@@ -331,4 +331,39 @@ export const esUs = {
DEPENDENCY_ERROR:
'No podemos procesar su pedido en este momento. Espere un momento e inténtelo de nuevo',
},
+ storefront: {
+ product: 'Producto',
+ sale: 'OFERTA',
+ noImage: 'Sin imagen',
+ noImageAvailable: 'Sin imagen disponible',
+ selectOptions: 'Seleccionar opciones',
+ adding: 'Agregando...',
+ addToCart: 'Agregar al carrito',
+ shoppingCart: 'Carrito de compras',
+ failedToLoadCart: 'Error al cargar el carrito:',
+ retry: 'Reintentar',
+ yourCartIsEmpty: 'Tu carrito está vacío',
+ addItemsToGetStarted: 'Agrega artículos para comenzar',
+ errorLoadingProducts: 'Error al cargar productos:',
+ errorLoadingProduct: 'Error al cargar producto:',
+ productNotFound: 'Producto no encontrado',
+ loadingVariantDetails: 'Cargando detalles de la variante...',
+ combinationNotAvailable:
+ 'Esta combinación no está disponible. Por favor selecciona opciones diferentes.',
+ variantsMatch:
+ 'variantes coinciden con tu selección. Selecciona más atributos para reducir las opciones.',
+ quantity: 'Cantidad',
+ addingToCart: 'Agregando al carrito...',
+ outOfStock: 'Agotado',
+ viewDetails: 'Ver detalles',
+ productType: 'Tipo de producto:',
+ productId: 'ID del producto:',
+ selectedSku: 'SKU seleccionado:',
+ stockStatus: 'Estado del stock:',
+ lowStock: 'Stock bajo',
+ inStock: 'En stock',
+ remove: 'Eliminar',
+ removing: 'Eliminando...',
+ checkout: 'Pagar',
+ },
};
diff --git a/packages/localizations/src/frCa.ts b/packages/localizations/src/frCa.ts
index ae5a2310..e798ec28 100644
--- a/packages/localizations/src/frCa.ts
+++ b/packages/localizations/src/frCa.ts
@@ -348,4 +348,39 @@ export const frCa = {
DEPENDENCY_ERROR:
'Nous ne pouvons pas traiter votre commande actuellement. Veuillez patienter un moment et réessayer',
},
+ storefront: {
+ product: 'Produit',
+ sale: 'SOLDE',
+ noImage: "Pas d'image",
+ noImageAvailable: 'Aucune image disponible',
+ selectOptions: 'Sélectionner les options',
+ adding: 'Ajout en cours...',
+ addToCart: 'Ajouter au panier',
+ shoppingCart: 'Panier',
+ failedToLoadCart: 'Échec du chargement du panier:',
+ retry: 'Réessayer',
+ yourCartIsEmpty: 'Votre panier est vide',
+ addItemsToGetStarted: 'Ajoutez des articles pour commencer',
+ errorLoadingProducts: 'Erreur lors du chargement des produits:',
+ errorLoadingProduct: 'Erreur lors du chargement du produit:',
+ productNotFound: 'Produit non trouvé',
+ loadingVariantDetails: 'Chargement des détails de la variante...',
+ combinationNotAvailable:
+ "Cette combinaison n'est pas disponible. Veuillez sélectionner d'autres options.",
+ variantsMatch:
+ "variantes correspondent à votre sélection. Sélectionnez plus d'attributs pour affiner.",
+ quantity: 'Quantité',
+ addingToCart: 'Ajout au panier...',
+ outOfStock: 'Rupture de stock',
+ viewDetails: 'Voir les détails',
+ productType: 'Type de produit:',
+ productId: 'ID du produit:',
+ selectedSku: 'SKU sélectionné:',
+ stockStatus: 'État du stock:',
+ lowStock: 'Stock faible',
+ inStock: 'En stock',
+ remove: 'Supprimer',
+ removing: 'Suppression...',
+ checkout: 'Commander',
+ },
};
diff --git a/packages/localizations/src/frFr.ts b/packages/localizations/src/frFr.ts
index d4ba9fc9..312851b6 100644
--- a/packages/localizations/src/frFr.ts
+++ b/packages/localizations/src/frFr.ts
@@ -349,4 +349,39 @@ export const frFr = {
DEPENDENCY_ERROR:
'Nous ne pouvons pas traiter votre commande actuellement. Veuillez patienter un moment et réessayer',
},
+ storefront: {
+ product: 'Produit',
+ sale: 'SOLDE',
+ noImage: "Pas d'image",
+ noImageAvailable: 'Aucune image disponible',
+ selectOptions: 'Sélectionner les options',
+ adding: 'Ajout en cours...',
+ addToCart: 'Ajouter au panier',
+ shoppingCart: 'Panier',
+ failedToLoadCart: 'Échec du chargement du panier:',
+ retry: 'Réessayer',
+ yourCartIsEmpty: 'Votre panier est vide',
+ addItemsToGetStarted: 'Ajoutez des articles pour commencer',
+ errorLoadingProducts: 'Erreur lors du chargement des produits:',
+ errorLoadingProduct: 'Erreur lors du chargement du produit:',
+ productNotFound: 'Produit non trouvé',
+ loadingVariantDetails: 'Chargement des détails de la variante...',
+ combinationNotAvailable:
+ "Cette combinaison n'est pas disponible. Veuillez sélectionner d'autres options.",
+ variantsMatch:
+ "variantes correspondent à votre sélection. Sélectionnez plus d'attributs pour affiner.",
+ quantity: 'Quantité',
+ addingToCart: 'Ajout au panier...',
+ outOfStock: 'Rupture de stock',
+ viewDetails: 'Voir les détails',
+ productType: 'Type de produit:',
+ productId: 'ID du produit:',
+ selectedSku: 'SKU sélectionné:',
+ stockStatus: 'État du stock:',
+ lowStock: 'Stock faible',
+ inStock: 'En stock',
+ remove: 'Supprimer',
+ removing: 'Suppression...',
+ checkout: 'Commander',
+ },
};
diff --git a/packages/localizations/src/idId.ts b/packages/localizations/src/idId.ts
index 29d9aff7..f5206314 100644
--- a/packages/localizations/src/idId.ts
+++ b/packages/localizations/src/idId.ts
@@ -324,4 +324,39 @@ export const idId = {
DEPENDENCY_ERROR:
'Kami tidak dapat memproses pesanan Anda saat ini. Silakan tunggu sebentar dan coba lagi',
},
+ storefront: {
+ product: 'Produk',
+ sale: 'OBRAL',
+ noImage: 'Tidak ada gambar',
+ noImageAvailable: 'Tidak ada gambar tersedia',
+ selectOptions: 'Pilih opsi',
+ adding: 'Menambahkan...',
+ addToCart: 'Tambahkan ke keranjang',
+ shoppingCart: 'Keranjang belanja',
+ failedToLoadCart: 'Gagal memuat keranjang:',
+ retry: 'Coba lagi',
+ yourCartIsEmpty: 'Keranjang Anda kosong',
+ addItemsToGetStarted: 'Tambahkan item untuk memulai',
+ errorLoadingProducts: 'Error memuat produk:',
+ errorLoadingProduct: 'Error memuat produk:',
+ productNotFound: 'Produk tidak ditemukan',
+ loadingVariantDetails: 'Memuat detail varian...',
+ combinationNotAvailable:
+ 'Kombinasi ini tidak tersedia. Silakan pilih opsi yang berbeda.',
+ variantsMatch:
+ 'varian cocok dengan pilihan Anda. Pilih lebih banyak atribut untuk mempersempit.',
+ quantity: 'Jumlah',
+ addingToCart: 'Menambahkan ke keranjang...',
+ outOfStock: 'Stok habis',
+ viewDetails: 'Lihat Detail',
+ productType: 'Jenis Produk:',
+ productId: 'ID Produk:',
+ selectedSku: 'SKU yang dipilih:',
+ stockStatus: 'Status Stok:',
+ lowStock: 'Stok rendah',
+ inStock: 'Tersedia',
+ remove: 'Hapus',
+ removing: 'Menghapus...',
+ checkout: 'Checkout',
+ },
};
diff --git a/packages/localizations/src/itIt.ts b/packages/localizations/src/itIt.ts
index a26a7298..20bba839 100644
--- a/packages/localizations/src/itIt.ts
+++ b/packages/localizations/src/itIt.ts
@@ -348,4 +348,39 @@ export const itIt = {
DEPENDENCY_ERROR:
'Non riusciamo a elaborare il tuo ordine in questo momento. Aspetta un momento e riprova',
},
+ storefront: {
+ product: 'Prodotto',
+ sale: 'SALDO',
+ noImage: 'Nessuna immagine',
+ noImageAvailable: 'Nessuna immagine disponibile',
+ selectOptions: 'Seleziona opzioni',
+ adding: 'Aggiunta in corso...',
+ addToCart: 'Aggiungi al carrello',
+ shoppingCart: 'Carrello',
+ failedToLoadCart: 'Caricamento carrello non riuscito:',
+ retry: 'Riprova',
+ yourCartIsEmpty: 'Il tuo carrello è vuoto',
+ addItemsToGetStarted: 'Aggiungi articoli per iniziare',
+ errorLoadingProducts: 'Errore nel caricamento dei prodotti:',
+ errorLoadingProduct: 'Errore nel caricamento del prodotto:',
+ productNotFound: 'Prodotto non trovato',
+ loadingVariantDetails: 'Caricamento dei dettagli della variante...',
+ combinationNotAvailable:
+ 'Questa combinazione non è disponibile. Si prega di selezionare opzioni diverse.',
+ variantsMatch:
+ 'varianti corrispondono alla tua selezione. Seleziona più attributi per restringere.',
+ quantity: 'Quantità',
+ addingToCart: 'Aggiunta al carrello...',
+ outOfStock: 'Esaurito',
+ viewDetails: 'Vedi dettagli',
+ productType: 'Tipo di prodotto:',
+ productId: 'ID prodotto:',
+ selectedSku: 'SKU selezionato:',
+ stockStatus: 'Stato delle scorte:',
+ lowStock: 'Scorte limitate',
+ inStock: 'Disponibile',
+ remove: 'Rimuovi',
+ removing: 'Rimozione...',
+ checkout: 'Acquista',
+ },
};
diff --git a/packages/localizations/src/ptBr.ts b/packages/localizations/src/ptBr.ts
index 78fa8072..79cd3b0d 100644
--- a/packages/localizations/src/ptBr.ts
+++ b/packages/localizations/src/ptBr.ts
@@ -329,4 +329,39 @@ export const ptBr = {
DEPENDENCY_ERROR:
'Não conseguimos processar seu pedido no momento. Aguarde um momento e tente novamente',
},
+ storefront: {
+ product: 'Produto',
+ sale: 'PROMOÇÃO',
+ noImage: 'Sem imagem',
+ noImageAvailable: 'Nenhuma imagem disponível',
+ selectOptions: 'Selecionar opções',
+ adding: 'Adicionando...',
+ addToCart: 'Adicionar ao carrinho',
+ shoppingCart: 'Carrinho de compras',
+ failedToLoadCart: 'Falha ao carregar carrinho:',
+ retry: 'Tentar novamente',
+ yourCartIsEmpty: 'Seu carrinho está vazio',
+ addItemsToGetStarted: 'Adicione itens para começar',
+ errorLoadingProducts: 'Erro ao carregar produtos:',
+ errorLoadingProduct: 'Erro ao carregar produto:',
+ productNotFound: 'Produto não encontrado',
+ loadingVariantDetails: 'Carregando detalhes da variante...',
+ combinationNotAvailable:
+ 'Esta combinação não está disponível. Por favor, selecione opções diferentes.',
+ variantsMatch:
+ 'variantes correspondem à sua seleção. Selecione mais atributos para refinar.',
+ quantity: 'Quantidade',
+ addingToCart: 'Adicionando ao carrinho...',
+ outOfStock: 'Fora de estoque',
+ viewDetails: 'Ver detalhes',
+ productType: 'Tipo de produto:',
+ productId: 'ID do produto:',
+ selectedSku: 'SKU selecionado:',
+ stockStatus: 'Status do estoque:',
+ lowStock: 'Estoque baixo',
+ inStock: 'Em estoque',
+ remove: 'Remover',
+ removing: 'Removendo...',
+ checkout: 'Finalizar compra',
+ },
};
diff --git a/packages/localizations/src/qaPs.ts b/packages/localizations/src/qaPs.ts
index c0e35fa8..0edfdcab 100644
--- a/packages/localizations/src/qaPs.ts
+++ b/packages/localizations/src/qaPs.ts
@@ -333,4 +333,39 @@ export const qaPs = {
DEPENDENCY_ERROR:
'موږ اوس ستاسو امر پروسس نشو کولی. مهرباني وکړئ یو شېبه انتظار وکړئ او بیا هڅه وکړئ',
},
+ storefront: {
+ product: '[Product]',
+ sale: '[SALE]',
+ noImage: '[No image]',
+ noImageAvailable: '[No image available]',
+ selectOptions: '[Select Options]',
+ adding: '[Adding...]',
+ addToCart: '[Add to Cart]',
+ shoppingCart: '[Shopping Cart]',
+ failedToLoadCart: '[Failed to load cart:]',
+ retry: '[Retry]',
+ yourCartIsEmpty: '[Your cart is empty]',
+ addItemsToGetStarted: '[Add items to get started]',
+ errorLoadingProducts: '[Error loading products:]',
+ errorLoadingProduct: '[Error loading product:]',
+ productNotFound: '[Product not found]',
+ loadingVariantDetails: '[Loading variant details...]',
+ combinationNotAvailable:
+ '[This combination is not available. Please select different options.]',
+ variantsMatch:
+ '[variants match your selection. Select more attributes to narrow down.]',
+ quantity: '[Quantity]',
+ addingToCart: '[Adding to Cart...]',
+ outOfStock: '[Out of Stock]',
+ viewDetails: '[View Details]',
+ productType: '[Product Type:]',
+ productId: '[Product ID:]',
+ selectedSku: '[Selected SKU:]',
+ stockStatus: '[Stock Status:]',
+ lowStock: '[Low Stock]',
+ inStock: '[In Stock]',
+ remove: '[Remove]',
+ removing: '[Removing...]',
+ checkout: '[Checkout]',
+ },
};
diff --git a/packages/localizations/src/trTr.ts b/packages/localizations/src/trTr.ts
index a2d1a5bd..ae4cb1d1 100644
--- a/packages/localizations/src/trTr.ts
+++ b/packages/localizations/src/trTr.ts
@@ -324,4 +324,39 @@ export const trTr = {
DEPENDENCY_ERROR:
'Şu anda siparişinizi işleme alamıyoruz. Lütfen bir dakika bekleyin ve tekrar deneyin',
},
+ storefront: {
+ product: 'Ürün',
+ sale: 'İNDİRİM',
+ noImage: 'Resim yok',
+ noImageAvailable: 'Resim mevcut değil',
+ selectOptions: 'Seçenekleri seçin',
+ adding: 'Ekleniyor...',
+ addToCart: 'Sepete ekle',
+ shoppingCart: 'Alışveriş sepeti',
+ failedToLoadCart: 'Sepet yüklenemedi:',
+ retry: 'Yeniden dene',
+ yourCartIsEmpty: 'Sepetiniz boş',
+ addItemsToGetStarted: 'Başlamak için ürün ekleyin',
+ errorLoadingProducts: 'Ürünler yüklenirken hata:',
+ errorLoadingProduct: 'Ürün yüklenirken hata:',
+ productNotFound: 'Ürün bulunamadı',
+ loadingVariantDetails: 'Varyant detayları yükleniyor...',
+ combinationNotAvailable:
+ 'Bu kombinasyon mevcut değil. Lütfen farklı seçenekler seçin.',
+ variantsMatch:
+ 'varyant seçiminizle eşleşiyor. Daraltmak için daha fazla özellik seçin.',
+ quantity: 'Miktar',
+ addingToCart: 'Sepete ekleniyor...',
+ outOfStock: 'Stokta yok',
+ viewDetails: 'Detayları Görüntüle',
+ productType: 'Ürün Tipi:',
+ productId: 'Ürün ID:',
+ selectedSku: 'Seçili SKU:',
+ stockStatus: 'Stok Durumu:',
+ lowStock: 'Düşük stok',
+ inStock: 'Stokta',
+ remove: 'Kaldır',
+ removing: 'Kaldırılıyor...',
+ checkout: 'Ödeme yap',
+ },
};
diff --git a/packages/localizations/src/viVn.ts b/packages/localizations/src/viVn.ts
index b5d031f1..e4660311 100644
--- a/packages/localizations/src/viVn.ts
+++ b/packages/localizations/src/viVn.ts
@@ -325,4 +325,39 @@ export const viVn = {
DEPENDENCY_ERROR:
'Chúng tôi không thể xử lý đơn hàng của bạn ngay bây giờ. Vui lòng đợi một chút và thử lại',
},
+ storefront: {
+ product: 'Sản phẩm',
+ sale: 'GIẢM GIÁ',
+ noImage: 'Không có hình ảnh',
+ noImageAvailable: 'Không có hình ảnh',
+ selectOptions: 'Chọn tùy chọn',
+ adding: 'Đang thêm...',
+ addToCart: 'Thêm vào giỏ hàng',
+ shoppingCart: 'Giỏ hàng',
+ failedToLoadCart: 'Không tải được giỏ hàng:',
+ retry: 'Thử lại',
+ yourCartIsEmpty: 'Giỏ hàng của bạn trống',
+ addItemsToGetStarted: 'Thêm sản phẩm để bắt đầu',
+ errorLoadingProducts: 'Lỗi tải sản phẩm:',
+ errorLoadingProduct: 'Lỗi tải sản phẩm:',
+ productNotFound: 'Không tìm thấy sản phẩm',
+ loadingVariantDetails: 'Đang tải chi tiết biến thể...',
+ combinationNotAvailable:
+ 'Sự kết hợp này không có sẵn. Vui lòng chọn tùy chọn khác.',
+ variantsMatch:
+ 'biến thể phù hợp với lựa chọn của bạn. Chọn thêm thuộc tính để thu hẹp.',
+ quantity: 'Số lượng',
+ addingToCart: 'Đang thêm vào giỏ hàng...',
+ outOfStock: 'Hết hàng',
+ viewDetails: 'Xem chi tiết',
+ productType: 'Loại sản phẩm:',
+ productId: 'ID sản phẩm:',
+ selectedSku: 'SKU đã chọn:',
+ stockStatus: 'Trạng thái kho:',
+ lowStock: 'Sắp hết hàng',
+ inStock: 'Còn hàng',
+ remove: 'Xóa',
+ removing: 'Đang xóa...',
+ checkout: 'Thanh toán',
+ },
};
diff --git a/packages/localizations/src/zhCn.ts b/packages/localizations/src/zhCn.ts
index 7e474e02..595fe1ed 100644
--- a/packages/localizations/src/zhCn.ts
+++ b/packages/localizations/src/zhCn.ts
@@ -314,4 +314,37 @@ export const zhCn = {
MISSING_SHIPPING_INFO: '配送地址或方式应用失败',
DEPENDENCY_ERROR: '我们目前无法处理您的订单。请稍等片刻再试',
},
+ storefront: {
+ product: '产品',
+ sale: '促销',
+ noImage: '无图片',
+ noImageAvailable: '无可用图片',
+ selectOptions: '选择选项',
+ adding: '正在添加...',
+ addToCart: '添加到购物车',
+ shoppingCart: '购物车',
+ failedToLoadCart: '加载购物车失败:',
+ retry: '重试',
+ yourCartIsEmpty: '您的购物车是空的',
+ addItemsToGetStarted: '添加商品以开始',
+ errorLoadingProducts: '加载产品时出错:',
+ errorLoadingProduct: '加载产品时出错:',
+ productNotFound: '未找到产品',
+ loadingVariantDetails: '正在加载变体详情...',
+ combinationNotAvailable: '此组合不可用。请选择不同的选项。',
+ variantsMatch: '个变体与您的选择匹配。选择更多属性以缩小范围。',
+ quantity: '数量',
+ addingToCart: '正在添加到购物车...',
+ outOfStock: '缺货',
+ viewDetails: '查看详情',
+ productType: '产品类型:',
+ productId: '产品ID:',
+ selectedSku: '已选SKU:',
+ stockStatus: '库存状态:',
+ lowStock: '库存不足',
+ inStock: '有货',
+ remove: '删除',
+ removing: '正在删除...',
+ checkout: '结账',
+ },
};
diff --git a/packages/localizations/src/zhSg.ts b/packages/localizations/src/zhSg.ts
index 6bd0ea7f..915dc466 100644
--- a/packages/localizations/src/zhSg.ts
+++ b/packages/localizations/src/zhSg.ts
@@ -314,4 +314,37 @@ export const zhSg = {
MISSING_SHIPPING_INFO: '配送地址或方式应用失败',
DEPENDENCY_ERROR: '我們目前無法處理您的訂單。請稍等片刻再試',
},
+ storefront: {
+ product: '产品',
+ sale: '促销',
+ noImage: '无图片',
+ noImageAvailable: '无可用图片',
+ selectOptions: '选择选项',
+ adding: '正在添加...',
+ addToCart: '添加到购物车',
+ shoppingCart: '购物车',
+ failedToLoadCart: '加载购物车失败:',
+ retry: '重试',
+ yourCartIsEmpty: '您的购物车是空的',
+ addItemsToGetStarted: '添加商品以开始',
+ errorLoadingProducts: '加载产品时出错:',
+ errorLoadingProduct: '加载产品时出错:',
+ productNotFound: '未找到产品',
+ loadingVariantDetails: '正在加载变体详情...',
+ combinationNotAvailable: '此组合不可用。请选择不同的选项。',
+ variantsMatch: '个变体与您的选择匹配。选择更多属性以缩小范围。',
+ quantity: '数量',
+ addingToCart: '正在添加到购物车...',
+ outOfStock: '缺货',
+ viewDetails: '查看详情',
+ productType: '产品类型:',
+ productId: '产品ID:',
+ selectedSku: '已选SKU:',
+ stockStatus: '库存状态:',
+ lowStock: '库存不足',
+ inStock: '有货',
+ remove: '删除',
+ removing: '正在删除...',
+ checkout: '结账',
+ },
};
diff --git a/packages/react/src/components/checkout/line-items/line-items.tsx b/packages/react/src/components/checkout/line-items/line-items.tsx
index 45a92786..196e9315 100644
--- a/packages/react/src/components/checkout/line-items/line-items.tsx
+++ b/packages/react/src/components/checkout/line-items/line-items.tsx
@@ -2,6 +2,7 @@
import { Image } from 'lucide-react';
import { useFormatCurrency } from '@/components/checkout/utils/format-currency';
+import { Button } from '@/components/ui/button.tsx';
import { useGoDaddyContext } from '@/godaddy-provider';
import type { SKUProduct } from '@/types';
@@ -57,12 +58,18 @@ export interface DraftOrderLineItemsProps {
items: Product[];
currencyCode?: string;
inputInMinorUnits?: boolean;
+ onRemoveFromCart?: (itemId: string) => void;
+ isRemovingFromCart?: boolean;
+ removingItemId?: string;
}
export function DraftOrderLineItems({
items,
currencyCode = 'USD',
inputInMinorUnits = false,
+ onRemoveFromCart,
+ isRemovingFromCart = false,
+ removingItemId,
}: DraftOrderLineItemsProps) {
const { t } = useGoDaddyContext();
const formatCurrency = useFormatCurrency();
@@ -126,12 +133,29 @@ export function DraftOrderLineItems({
) : null}
- {t.general.quantity}: {item.quantity}
+ {t.general.quantity}: {item.quantity} ·{' '}
+ {onRemoveFromCart ? (
+
+ ) : null}
{item.originalPrice && item.quantity ? (
-
-
+
+
{formatCurrency({
amount: item.originalPrice * item.quantity,
diff --git a/packages/react/src/components/checkout/utils/checkout-transformers.ts b/packages/react/src/components/checkout/utils/checkout-transformers.ts
index 5382367c..d7d36314 100644
--- a/packages/react/src/components/checkout/utils/checkout-transformers.ts
+++ b/packages/react/src/components/checkout/utils/checkout-transformers.ts
@@ -157,7 +157,7 @@ export function mapSkusToItemsDisplay(
return {
id: orderItem.id || '',
name: skuDetails?.label || orderItem.name || '',
- image: orderItem.details?.productAssetUrl || skuDetails?.mediaUrls?.[0],
+ image: orderItem.details?.productAssetUrl,
quantity: orderItem.quantity || 0,
originalPrice:
(orderItem.totals?.subTotal?.value ?? 0) / (orderItem.quantity || 0),
diff --git a/packages/react/src/components/storefront/cart.tsx b/packages/react/src/components/storefront/cart.tsx
index 58a8943e..9d0472ff 100644
--- a/packages/react/src/components/storefront/cart.tsx
+++ b/packages/react/src/components/storefront/cart.tsx
@@ -1,55 +1,131 @@
'use client';
+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import { Loader2, ShoppingCart } from 'lucide-react';
+import { useEffect, useState } from 'react';
import type { Product } from '@/components/checkout/line-items/line-items';
import { CartLineItems } from '@/components/storefront/cart-line-items';
import { CartTotals } from '@/components/storefront/cart-totals';
+import { Button } from '@/components/ui/button';
import {
Sheet,
SheetContent,
+ SheetFooter,
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet';
import { useGoDaddyContext } from '@/godaddy-provider';
+import { getCartOrderId } from '@/lib/cart-storage';
+import { deleteCartLineItem, getCartOrder } from '@/lib/godaddy/godaddy';
interface CartProps {
open: boolean;
onOpenChange: (open: boolean) => void;
+ onCheckout?: (orderId: string) => void;
+ isCheckingOut?: boolean;
}
-export function Cart({ open, onOpenChange }: CartProps) {
- // Mock data
- const items: Product[] = [
- {
- id: 'LineItem_2y0l7o6Oi4BW6fpSiKPX1hhBccU',
- name: 'Box of cookies',
- image:
- 'https://isteam.dev-wsimg.com/ip/2f2e05ec-de6f-4a89-90f2-038c749655b0/cookies.webp',
- quantity: 2,
- originalPrice: 10.99,
- price: 10.99,
- notes: [],
- },
- {
- id: 'LineItem_2y0l9FykA04qp2pC6y3YZ0TbZFD',
- name: 'Cupcakes',
- image:
- 'https://isteam.dev-wsimg.com/ip/2f2e05ec-de6f-4a89-90f2-038c749655b0/cupcakes.webp/:/rs=w:600,h:600',
- quantity: 1,
- originalPrice: 5.99,
- price: 5.99,
- notes: [],
+export function Cart({
+ open,
+ onOpenChange,
+ onCheckout,
+ isCheckingOut = false,
+}: CartProps) {
+ const context = useGoDaddyContext();
+ const queryClient = useQueryClient();
+ const [cartOrderId, setCartOrderId] = useState(null);
+
+ useEffect(() => {
+ setCartOrderId(getCartOrderId());
+ }, []);
+
+ // Fetch cart order
+ const {
+ data: cartData,
+ isLoading,
+ error,
+ } = useQuery({
+ queryKey: ['cart-order', cartOrderId],
+ queryFn: () =>
+ getCartOrder(
+ cartOrderId!,
+ context.storeId!,
+ context.clientId!,
+ context?.apiHost
+ ),
+ enabled: !!cartOrderId && !!context.storeId && !!context.clientId,
+ });
+
+ // Delete line item mutation
+ const deleteMutation = useMutation({
+ mutationFn: (lineItemId: string) =>
+ deleteCartLineItem(
+ { id: lineItemId, orderId: cartOrderId! },
+ context.storeId!,
+ context.clientId!,
+ context?.apiHost
+ ),
+ onSuccess: () => {
+ // Invalidate cart query to refetch
+ queryClient.invalidateQueries({ queryKey: ['cart-order', cartOrderId] });
},
- ];
+ });
+
+ const handleRemoveFromCart = (itemId: string) => {
+ deleteMutation.mutate(itemId);
+ };
+
+ const order = cartData?.orderById;
+
+ const { t } = useGoDaddyContext();
+
+ // Transform cart line items to Product format for CartLineItems component
+ const items: Product[] =
+ order?.lineItems?.map(item => ({
+ id: item.id,
+ name: item.name || t.storefront.product,
+ image: item.details?.productAssetUrl || '',
+ quantity: item.quantity || 0,
+ originalPrice: (item.totals?.subTotal?.value || 0) / (item.quantity || 1),
+ price: (item.totals?.subTotal?.value || 0) / (item.quantity || 1),
+ selectedOptions:
+ item?.details?.selectedOptions?.map(option => ({
+ attribute: option.attribute || '',
+ values: option.values || [],
+ })) || [],
+ addons: item.details?.selectedAddons?.map(addon => ({
+ attribute: addon.attribute || '',
+ sku: addon.sku || '',
+ values: addon.values?.map(value => ({
+ costAdjustment: value.costAdjustment
+ ? {
+ currencyCode: value.costAdjustment.currencyCode ?? undefined,
+ value: value.costAdjustment.value ?? undefined,
+ }
+ : undefined,
+ name: value.name ?? undefined,
+ })),
+ })),
+ })) || [];
+
+ // Calculate totals
+ const itemCount = items.reduce((sum, item) => sum + item.quantity, 0);
+ const currencyCode = order?.totals?.total?.currencyCode || 'USD';
+ const subtotal = order?.totals?.subTotal?.value || 0;
+ const shipping = order?.totals?.shippingTotal?.value || 0;
+ const taxes = order?.totals?.taxTotal?.value || 0;
+ const discount = order?.totals?.discountTotal?.value || 0;
+ const total = order?.totals?.total?.value || 0;
const totals = {
- subtotal: 27.97,
- discount: 0,
- shipping: 0,
- currencyCode: 'USD',
- itemCount: 3,
- total: 27.97,
+ subtotal,
+ discount,
+ shipping,
+ currencyCode,
+ itemCount,
+ total,
tip: 0,
- taxes: 0,
+ taxes,
enableDiscounts: false,
enableTaxes: true,
isTaxLoading: false,
@@ -59,11 +135,79 @@ export function Cart({ open, onOpenChange }: CartProps) {
- Shopping Cart
+ {t.storefront.shoppingCart}
-
-
+ {isLoading && (
+
+
+
+ )}
+
+ {!isLoading && error && (
+
+
+ {t.storefront.failedToLoadCart} {(error as Error).message}
+
+
+
+ )}
+
+ {!isLoading && !error && (!cartOrderId || items.length === 0) && (
+
+
+
+ {t.storefront.yourCartIsEmpty}
+
+
+ {t.storefront.addItemsToGetStarted}
+
+
+ )}
+
+ {!isLoading && !error && cartOrderId && items.length > 0 && (
+ <>
+
+
+ {onCheckout ? (
+
+
+
+ ) : null}
+ >
+ )}
diff --git a/packages/react/src/components/storefront/hooks/use-add-to-cart.ts b/packages/react/src/components/storefront/hooks/use-add-to-cart.ts
new file mode 100644
index 00000000..34c9d01c
--- /dev/null
+++ b/packages/react/src/components/storefront/hooks/use-add-to-cart.ts
@@ -0,0 +1,120 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { useGoDaddyContext } from '@/godaddy-provider';
+import { getCartOrderId, setCartOrderId } from '@/lib/cart-storage';
+import { addCartLineItem, createCartOrder } from '@/lib/godaddy/godaddy';
+
+export interface AddToCartInput {
+ skuId: string;
+ name: string;
+ quantity: number;
+ productAssetUrl?: string;
+}
+
+export interface UseAddToCartOptions {
+ onSuccess?: () => void;
+ onError?: (error: Error) => void;
+}
+
+export function useAddToCart(options?: UseAddToCartOptions) {
+ const context = useGoDaddyContext();
+ const queryClient = useQueryClient();
+
+ // Create cart order mutation
+ const createCartMutation = useMutation({
+ mutationFn: () =>
+ createCartOrder(
+ {
+ context: {
+ storeId: context?.storeId || '',
+ channelId: context?.channelId || '',
+ },
+ totals: {
+ subTotal: { value: 0, currencyCode: 'USD' },
+ shippingTotal: { value: 0, currencyCode: 'USD' },
+ discountTotal: { value: 0, currencyCode: 'USD' },
+ feeTotal: { value: 0, currencyCode: 'USD' },
+ taxTotal: { value: 0, currencyCode: 'USD' },
+ total: { value: 0, currencyCode: 'USD' },
+ },
+ },
+ context.storeId!,
+ context.clientId!,
+ context?.apiHost
+ ),
+ onSuccess: data => {
+ if (data.addDraftOrder?.id) {
+ setCartOrderId(data.addDraftOrder.id);
+ }
+ },
+ });
+
+ // Add line item mutation
+ const addLineItemMutation = useMutation({
+ mutationFn: ({
+ orderId,
+ input,
+ }: {
+ orderId: string;
+ input: AddToCartInput;
+ }) =>
+ addCartLineItem(
+ {
+ orderId,
+ skuId: input.skuId,
+ name: input.name,
+ quantity: input.quantity,
+ fulfillmentMode: 'SHIP',
+ status: 'DRAFT',
+ details: {
+ productAssetUrl: input.productAssetUrl || undefined,
+ },
+ },
+ context.storeId!,
+ context.clientId!,
+ context?.apiHost
+ ),
+ onSuccess: () => {
+ // Invalidate cart query to refresh
+ const cartOrderId = getCartOrderId();
+ queryClient.invalidateQueries({ queryKey: ['cart-order', cartOrderId] });
+
+ // Call success callback
+ options?.onSuccess?.();
+ },
+ onError: error => {
+ // Call error callback
+ options?.onError?.(error as Error);
+ },
+ });
+
+ const addToCart = async (input: AddToCartInput) => {
+ if (!context.storeId || !context.clientId) {
+ const error = new Error('Store ID and Client ID are required');
+ options?.onError?.(error);
+ throw error;
+ }
+ let cartOrderId = getCartOrderId();
+
+ // Create cart if it doesn't exist
+ if (!cartOrderId) {
+ const result = await createCartMutation.mutateAsync();
+ cartOrderId = result.addDraftOrder?.id || null;
+
+ if (!cartOrderId) {
+ const error = new Error('Failed to create cart');
+ options?.onError?.(error);
+ throw error;
+ }
+ }
+
+ // Add line item to cart
+ await addLineItemMutation.mutateAsync({ orderId: cartOrderId, input });
+ };
+
+ return {
+ addToCart,
+ isLoading: createCartMutation.isPending || addLineItemMutation.isPending,
+ isCreatingCart: createCartMutation.isPending,
+ isAddingItem: addLineItemMutation.isPending,
+ };
+}
diff --git a/packages/react/src/components/storefront/product-card.tsx b/packages/react/src/components/storefront/product-card.tsx
index 0b642ec1..107d6442 100644
--- a/packages/react/src/components/storefront/product-card.tsx
+++ b/packages/react/src/components/storefront/product-card.tsx
@@ -1,21 +1,31 @@
'use client';
-import { ChevronRight, ShoppingBag } from 'lucide-react';
+import { ChevronRight, Loader2, ShoppingBag } from 'lucide-react';
import { useFormatCurrency } from '@/components/checkout/utils/format-currency';
+import { useAddToCart } from '@/components/storefront/hooks/use-add-to-cart';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { RouterLink } from '@/components/ui/link';
+import { useGoDaddyContext } from '@/godaddy-provider';
import { SKUGroup } from '@/types.ts';
interface ProductCardProps {
product: SKUGroup;
href?: string;
+ onAddToCartSuccess?: () => void;
+ onAddToCartError?: (error: Error) => void;
}
-export function ProductCard({ product, href }: ProductCardProps) {
+export function ProductCard({
+ product,
+ href,
+ onAddToCartSuccess,
+ onAddToCartError,
+}: ProductCardProps) {
const formatCurrency = useFormatCurrency();
- const title = product?.label || product?.name || 'Product';
+ const { t } = useGoDaddyContext();
+ const title = product?.label || product?.name || t.storefront.product;
const description = product?.description || '';
const priceMin = product?.priceRange?.min || 0;
const priceMax = product?.priceRange?.max || priceMin;
@@ -28,16 +38,99 @@ export function ProductCard({ product, href }: ProductCardProps) {
edge => edge?.node?.type === 'IMAGE'
)?.node?.url;
- const handleAddToCart = (e: React.MouseEvent) => {
+ // Get first SKU and check inventory
+ const firstSku = product?.skus?.edges?.[0]?.node;
+ const skuId = firstSku?.id || product?.id || '';
+
+ // Check available inventory for first SKU
+ const inventoryCounts = firstSku?.inventoryCounts?.edges || [];
+ const availableInventory =
+ inventoryCounts.find((edge: any) => edge?.node?.type === 'AVAILABLE')?.node
+ ?.quantity || 0;
+
+ const isFirstSkuInStock = availableInventory > 0;
+ const hasMultipleSkus = (product?.skus?.edges?.length || 0) > 1;
+
+ // Use shared add to cart hook
+ const { addToCart, isLoading: isAddingToCart } = useAddToCart({
+ onSuccess: onAddToCartSuccess,
+ onError: onAddToCartError,
+ });
+
+ const handleAddToCart = async (e: React.MouseEvent) => {
e.preventDefault();
+ e.stopPropagation();
+
+ if (!skuId) {
+ return;
+ }
+
+ await addToCart({
+ skuId,
+ name: title,
+ quantity: 1,
+ productAssetUrl: imageUrl || undefined,
+ });
+ };
+
+ // Determine which button to show based on inventory and variants
+ const getActionButton = () => {
+ // If product has options (future use case), show "Select Options"
+ if (hasOptions) {
+ return (
+
+ );
+ }
+
+ // If first SKU is out of stock
+ if (!isFirstSkuInStock) {
+ // If there are multiple SKUs and an href, suggest viewing details
+ if (hasMultipleSkus && href) {
+ return (
+
+ );
+ }
+ // Otherwise show out of stock button
+ return (
+
+ );
+ }
+
+ // First SKU is in stock, show "Add to Cart"
+ return (
+
+ );
};
const cardContent = (
<>
{isOnSale && (
-
- SALE
+
+ {t.storefront.sale}
)}
{imageUrl ? (
@@ -48,7 +141,7 @@ export function ProductCard({ product, href }: ProductCardProps) {
/>
) : (
- No image
+ {t.storefront.noImage}
)}
@@ -69,17 +162,7 @@ export function ProductCard({ product, href }: ProductCardProps) {
inputInMinorUnits: true,
})}
- {hasOptions ? (
-
- ) : (
-
- )}
+ {getActionButton()}
>
diff --git a/packages/react/src/components/storefront/product-details.tsx b/packages/react/src/components/storefront/product-details.tsx
index 31201a4a..7e072ca8 100644
--- a/packages/react/src/components/storefront/product-details.tsx
+++ b/packages/react/src/components/storefront/product-details.tsx
@@ -1,9 +1,10 @@
'use client';
import { useQuery } from '@tanstack/react-query';
-import { Minus, Plus, ShoppingCart } from 'lucide-react';
+import { Loader2, Minus, Plus, ShoppingCart } from 'lucide-react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useFormatCurrency } from '@/components/checkout/utils/format-currency';
+import { useAddToCart } from '@/components/storefront/hooks/use-add-to-cart';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
@@ -24,6 +25,8 @@ interface ProductDetailsProps {
productId: string;
storeId?: string;
clientId?: string;
+ onAddToCartSuccess?: () => void;
+ onAddToCartError?: (error: Error) => void;
}
// Flattened attribute structure for UI (transforms edges/node to flat array)
@@ -119,8 +122,11 @@ export function ProductDetails({
productId,
storeId: storeIdProp,
clientId: clientIdProp,
+ onAddToCartSuccess,
+ onAddToCartError,
}: ProductDetailsProps) {
const context = useGoDaddyContext();
+ const { t } = context;
const formatCurrency = useFormatCurrency();
// Props take priority over context values
@@ -145,6 +151,15 @@ export function ProductDetails({
return result;
});
+ // Use shared add to cart hook
+ const { addToCart, isLoading: isAddingToCart } = useAddToCart({
+ onSuccess: () => {
+ setQuantity(1); // Reset quantity
+ onAddToCartSuccess?.();
+ },
+ onError: onAddToCartError,
+ });
+
// Update URL when variant params change
const setVariantParams = useCallback(
(updates: Record
) => {
@@ -215,7 +230,11 @@ export function ProductDetails({
],
queryFn: () =>
getSkuGroup(
- { id: productId!, attributeValues: selectedAttributeValues },
+ {
+ id: productId!,
+ attributeValues: selectedAttributeValues,
+ ...(!selectedAttributeValues.length ? { first: 2 } : {}),
+ },
storeId!,
clientId!,
context?.apiHost
@@ -297,7 +316,7 @@ export function ProductDetails({
return (
- Error loading product: {error.message}
+ {t.storefront.errorLoadingProduct} {error.message}
);
@@ -309,13 +328,13 @@ export function ProductDetails({
return (
- Product not found
+ {t.storefront.productNotFound}
);
}
- const title = product?.label || product?.name || 'Product';
+ const title = product?.label || product?.name || t.storefront.product;
const description = product?.description || '';
const htmlDescription = product?.htmlDescription || '';
@@ -376,10 +395,19 @@ export function ProductDetails({
})()
: false;
- const canAddToCart = !isOutOfStock && (!attributes.length || selectedSku);
+ const canAddToCart = !isOutOfStock && Boolean(selectedSku);
+
+ const handleAddToCart = async () => {
+ if (!canAddToCart) {
+ return;
+ }
- const handleAddToCart = () => {
- // Placeholder for add to cart functionality
+ await addToCart({
+ skuId: selectedSku?.id || product?.skus?.edges?.[0]?.node?.id || '',
+ name: title,
+ quantity,
+ productAssetUrl: images[0] || undefined,
+ });
};
return (
@@ -389,8 +417,11 @@ export function ProductDetails({
{/* Main Image Carousel */}
{isOnSale && (
-
- SALE
+
+ {t.storefront.sale}
)}
- No image available
+ {t.storefront.noImageAvailable}
@@ -501,7 +532,7 @@ export function ProductDetails({
{/* Product Information */}
-
+
{title}
@@ -580,19 +611,17 @@ export function ProductDetails({
{isSkuLoading && (
- Loading variant details...
+ {t.storefront.loadingVariantDetails}
)}
{!isSkuLoading && matchedSkus.length === 0 && (
- This combination is not available. Please select different
- options.
+ {t.storefront.combinationNotAvailable}
)}
{!isSkuLoading && matchedSkus.length > 1 && (
- {matchedSkus.length} variants match your selection. Select
- more attributes to narrow down.
+ {matchedSkus.length} {t.storefront.variantsMatch}
)}
@@ -603,7 +632,7 @@ export function ProductDetails({
{/* Quantity Selector */}
{/* Additional Product Information */}
{product?.type && (
-
Product Type:
+
+ {t.storefront.productType}
+
{product.type}
@@ -650,7 +690,9 @@ export function ProductDetails({
)}
{product?.id && (
-
Product ID:
+
+ {t.storefront.productId}
+
{product.id}
@@ -659,7 +701,9 @@ export function ProductDetails({
{selectedSku && (
<>
-
Selected SKU:
+
+ {t.storefront.selectedSku}
+
{selectedSku.code}
@@ -667,17 +711,20 @@ export function ProductDetails({
{selectedSku.inventoryCounts?.edges &&
selectedSku.inventoryCounts.edges.length > 0 && (
- Stock Status:
+
+ {t.storefront.stockStatus}
+
{(() => {
const availableCount =
selectedSku.inventoryCounts.edges.find(
edge => edge?.node?.type === 'AVAILABLE'
)?.node?.quantity ?? 0;
- if (availableCount === 0) return 'Out of Stock';
+ if (availableCount === 0)
+ return t.storefront.outOfStock;
if (availableCount < 10)
- return `Low Stock (${availableCount})`;
- return 'In Stock';
+ return `${t.storefront.lowStock} (${availableCount})`;
+ return t.storefront.inStock;
})()}
diff --git a/packages/react/src/components/storefront/product-grid.tsx b/packages/react/src/components/storefront/product-grid.tsx
index dc0fe042..d73607f0 100644
--- a/packages/react/src/components/storefront/product-grid.tsx
+++ b/packages/react/src/components/storefront/product-grid.tsx
@@ -11,6 +11,8 @@ interface ProductGridProps {
clientId?: string;
first?: number;
getProductHref?: (productId: string) => string;
+ onAddToCartSuccess?: () => void;
+ onAddToCartError?: (error: Error) => void;
}
function ProductGridSkeleton({ count = 6 }: { count?: number }) {
@@ -34,8 +36,11 @@ export function ProductGrid({
clientId: clientIdProp,
first = 100,
getProductHref,
+ onAddToCartSuccess,
+ onAddToCartError,
}: ProductGridProps) {
const context = useGoDaddyContext();
+ const { t } = context;
const storeId = storeIdProp || context.storeId;
const clientId = clientIdProp || context.clientId;
@@ -51,7 +56,11 @@ export function ProductGrid({
}
if (error) {
- return
Error loading products: {error.message}
;
+ return (
+
+ {t.storefront.errorLoadingProducts} {error.message}
+
+ );
}
const skuGroups = data?.skuGroups?.edges;
@@ -63,7 +72,15 @@ export function ProductGrid({
if (!group?.id) return null;
const href = getProductHref?.(group.id);
- return
;
+ return (
+
+ );
})}
);
diff --git a/packages/react/src/components/ui/badge.tsx b/packages/react/src/components/ui/badge.tsx
index 19b5addc..47d4ca2f 100644
--- a/packages/react/src/components/ui/badge.tsx
+++ b/packages/react/src/components/ui/badge.tsx
@@ -13,6 +13,8 @@ const badgeVariants = cva(
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
destructive:
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
+ accent:
+ 'border-transparent bg-accent text-accent-foreground hover:bg-accent/80',
outline: 'text-foreground',
},
},
diff --git a/packages/react/src/components/ui/sheet.tsx b/packages/react/src/components/ui/sheet.tsx
index 408e6676..f867253c 100644
--- a/packages/react/src/components/ui/sheet.tsx
+++ b/packages/react/src/components/ui/sheet.tsx
@@ -68,7 +68,7 @@ const SheetContent = React.forwardRef<
className={cn(sheetVariants({ side }), className)}
{...props}
>
-
+
{t.ui.accessibility.close}
diff --git a/packages/react/src/globals.css b/packages/react/src/globals.css
index 89152395..ac3016a4 100644
--- a/packages/react/src/globals.css
+++ b/packages/react/src/globals.css
@@ -1,6 +1,80 @@
@import "tailwindcss" source(none);
@source "./components/";
+@keyframes enter {
+ from {
+ opacity: 0;
+ transform: var(--tw-enter-transform);
+ }
+}
+
+@keyframes exit {
+ to {
+ opacity: 0;
+ transform: var(--tw-exit-transform);
+ }
+}
+
+@utility animate-in {
+ animation-name: enter;
+ animation-duration: 0.15s;
+ --tw-enter-transform: none;
+}
+
+@utility animate-out {
+ animation-name: exit;
+ animation-duration: 0.15s;
+ --tw-exit-transform: none;
+}
+
+@utility fade-in-0 {
+ --tw-enter-opacity: 0;
+}
+
+@utility fade-out-0 {
+ --tw-exit-opacity: 0;
+}
+
+@utility slide-in-from-right {
+ --tw-enter-transform: translateX(100%);
+}
+
+@utility slide-out-to-right {
+ --tw-exit-transform: translateX(100%);
+}
+
+@utility slide-in-from-left {
+ --tw-enter-transform: translateX(-100%);
+}
+
+@utility slide-out-to-left {
+ --tw-exit-transform: translateX(-100%);
+}
+
+@utility slide-in-from-top {
+ --tw-enter-transform: translateY(-100%);
+}
+
+@utility slide-out-to-top {
+ --tw-exit-transform: translateY(-100%);
+}
+
+@utility slide-in-from-bottom {
+ --tw-enter-transform: translateY(100%);
+}
+
+@utility slide-out-to-bottom {
+ --tw-exit-transform: translateY(100%);
+}
+
+@utility duration-300 {
+ animation-duration: 300ms;
+}
+
+@utility duration-500 {
+ animation-duration: 500ms;
+}
+
@font-face {
font-family: "GD Sherpa";
src: url("https://d85ecz8votkqa.cloudfront.net/fonts/gd-sherpa-regular.eot"); /* IE9 Compat Modes */
diff --git a/packages/react/src/godaddy-provider.tsx b/packages/react/src/godaddy-provider.tsx
index 6faea256..c13fd245 100644
--- a/packages/react/src/godaddy-provider.tsx
+++ b/packages/react/src/godaddy-provider.tsx
@@ -73,6 +73,7 @@ interface GoDaddyContextValue {
apiHost?: string;
clientId?: string;
storeId?: string;
+ channelId?: string;
locale?: string;
Link?: React.ComponentType;
}
@@ -101,13 +102,13 @@ export interface GoDaddyProviderProps {
*
* Internal devs can set to:
* - "http://localhost:3000" for local development
- * - "https://checkout.commerce.api.dev-godaddy.com" for DEV environment
- * - "https://checkout.commerce.api.test-godaddy.com" for TEST environment
*/
apiHost?: string;
clientId?: string;
storeId?: string;
+ channelId?: string;
locale?: string;
+
queryClient?: QueryClient;
Link?: React.ComponentType;
children: QueryClientProviderProps['children'];
@@ -120,6 +121,7 @@ export function GoDaddyProvider({
apiHost,
clientId,
storeId,
+ channelId,
locale = 'en-US',
queryClient: providedQueryClient,
Link,
@@ -214,6 +216,7 @@ export function GoDaddyProvider({
apiHost,
clientId,
storeId,
+ channelId,
locale,
Link,
}}
diff --git a/packages/react/src/lib/cart-storage.ts b/packages/react/src/lib/cart-storage.ts
new file mode 100644
index 00000000..3f4c2e2b
--- /dev/null
+++ b/packages/react/src/lib/cart-storage.ts
@@ -0,0 +1,78 @@
+const CART_ORDER_ID_KEY = 'godaddy_cart_order_id';
+const CART_CREATED_AT_KEY = 'godaddy_cart_created_at';
+const CART_TTL = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds
+
+/**
+ * Get the cart order ID from localStorage.
+ * Returns null if the cart doesn't exist or has expired.
+ */
+export function getCartOrderId(): string | null {
+ if (typeof window === 'undefined') {
+ // SSR safety
+ return null;
+ }
+
+ const orderId = localStorage.getItem(CART_ORDER_ID_KEY);
+ const createdAt = localStorage.getItem(CART_CREATED_AT_KEY);
+
+ // No cart exists
+ if (!orderId) {
+ return null;
+ }
+
+ // Check if cart has expired (30 days old)
+ if (createdAt) {
+ const age = Date.now() - parseInt(createdAt, 10);
+ if (age > CART_TTL) {
+ // Cart expired, clean it up
+ clearCartOrderId();
+ return null;
+ }
+ }
+
+ return orderId;
+}
+
+/**
+ * Save the cart order ID to localStorage with a timestamp.
+ */
+export function setCartOrderId(orderId: string): void {
+ if (typeof window === 'undefined') {
+ // SSR safety
+ return;
+ }
+
+ localStorage.setItem(CART_ORDER_ID_KEY, orderId);
+ localStorage.setItem(CART_CREATED_AT_KEY, Date.now().toString());
+}
+
+/**
+ * Remove the cart order ID and timestamp from localStorage.
+ */
+export function clearCartOrderId(): void {
+ if (typeof window === 'undefined') {
+ // SSR safety
+ return;
+ }
+
+ localStorage.removeItem(CART_ORDER_ID_KEY);
+ localStorage.removeItem(CART_CREATED_AT_KEY);
+}
+
+/**
+ * Get the age of the current cart in days.
+ * Returns null if no cart exists.
+ */
+export function getCartAge(): number | null {
+ if (typeof window === 'undefined') {
+ return null;
+ }
+
+ const createdAt = localStorage.getItem(CART_CREATED_AT_KEY);
+ if (!createdAt) {
+ return null;
+ }
+
+ const ageMs = Date.now() - parseInt(createdAt, 10);
+ return Math.floor(ageMs / (24 * 60 * 60 * 1000)); // Convert to days
+}
diff --git a/packages/react/src/lib/godaddy/catalog-storefront-env.ts b/packages/react/src/lib/godaddy/catalog-storefront-env.ts
index dcce0046..5d61fca3 100644
--- a/packages/react/src/lib/godaddy/catalog-storefront-env.ts
+++ b/packages/react/src/lib/godaddy/catalog-storefront-env.ts
@@ -1325,15 +1325,6 @@ const introspection = {
],
isDeprecated: false,
},
- {
- name: 'status',
- type: {
- kind: 'SCALAR',
- name: 'String',
- },
- args: [],
- isDeprecated: false,
- },
{
name: 'updatedAt',
type: {
@@ -2528,15 +2519,6 @@ const introspection = {
],
isDeprecated: false,
},
- {
- name: 'status',
- type: {
- kind: 'SCALAR',
- name: 'String',
- },
- args: [],
- isDeprecated: false,
- },
{
name: 'updatedAt',
type: {
@@ -8454,6 +8436,24 @@ const introspection = {
args: [],
isDeprecated: true,
},
+ {
+ name: 'skuCount',
+ type: {
+ kind: 'SCALAR',
+ name: 'Int',
+ },
+ args: [
+ {
+ name: 'status',
+ type: {
+ kind: 'INPUT_OBJECT',
+ name: 'SKUStatusFilter',
+ },
+ defaultValue: '{in: ["ACTIVE", "DRAFT"]}',
+ },
+ ],
+ isDeprecated: false,
+ },
{
name: 'skus',
type: {
diff --git a/packages/react/src/lib/godaddy/catalog-storefront-queries.ts b/packages/react/src/lib/godaddy/catalog-storefront-queries.ts
index fc1f4b02..eab1ed43 100644
--- a/packages/react/src/lib/godaddy/catalog-storefront-queries.ts
+++ b/packages/react/src/lib/godaddy/catalog-storefront-queries.ts
@@ -1,7 +1,7 @@
import { graphql } from '@/lib/gql/gql-catalog-storefront.tada.ts';
export const SkuGroupsQuery = graphql(`
- query SkuGroups($first: Int, $after: String, $id: SKUGroupIdsFilter, $attributeValues: [String!] = []) {
+ query SkuGroups($first: Int, $after: String, $id: SKUGroupIdsFilter) {
skuGroups(first: $first, after: $after, id: $id) {
edges {
cursor
@@ -48,12 +48,21 @@ export const SkuGroupsQuery = graphql(`
}
}
}
- skus(attributeValues: { has: $attributeValues }) {
+ skus(first: 2) {
edges {
node {
id
label
name
+ inventoryCounts {
+ edges {
+ node {
+ id
+ quantity
+ type
+ }
+ }
+ }
}
}
}
@@ -70,7 +79,7 @@ export const SkuGroupsQuery = graphql(`
`);
export const SkuGroupQuery = graphql(`
- query SkuGroup($id: String!, $attributeValues: [String!] = []) {
+ query SkuGroup($id: String!, $first: Int, $attributeValues: [String!] = []) {
skuGroup(id: $id) {
id
name
@@ -114,12 +123,24 @@ export const SkuGroupQuery = graphql(`
}
}
}
- skus(attributeValues: { has: $attributeValues }) {
+ skus(
+ attributeValues: { has: $attributeValues }
+ first: $first
+ ) {
edges {
node {
id
label
name
+ inventoryCounts {
+ edges {
+ node {
+ id
+ quantity
+ type
+ }
+ }
+ }
}
}
}
diff --git a/packages/react/src/lib/godaddy/checkout-mutations.ts b/packages/react/src/lib/godaddy/checkout-mutations.ts
index 23cc9741..d493425c 100644
--- a/packages/react/src/lib/godaddy/checkout-mutations.ts
+++ b/packages/react/src/lib/godaddy/checkout-mutations.ts
@@ -5,6 +5,7 @@ export const CreateCheckoutSessionMutation = graphql(`
createCheckoutSession(input: $input) {
id
token
+ url
sourceApp
returnUrl
successUrl
diff --git a/packages/react/src/lib/godaddy/godaddy.ts b/packages/react/src/lib/godaddy/godaddy.ts
index ece3f3ab..5d45662b 100644
--- a/packages/react/src/lib/godaddy/godaddy.ts
+++ b/packages/react/src/lib/godaddy/godaddy.ts
@@ -6,15 +6,28 @@ import {
SkuGroupsQuery,
SkuQuery,
} from '@/lib/godaddy/catalog-storefront-queries.ts';
+import {
+ AddCartOrderMutation,
+ AddLineItemBySkuIdMutation,
+ ApplyDiscountCodesMutation,
+ DeleteLineItemByIdMutation,
+ UpdateCartOrderMutation,
+ UpdateLineItemByIdMutation,
+} from '@/lib/godaddy/orders-storefront-mutations.ts';
+import { GetCartOrderQuery } from '@/lib/godaddy/orders-storefront-queries.ts';
import { graphqlRequestWithErrors } from '@/lib/graphql-with-errors';
import type {
+ AddCartOrderInput,
+ AddLineItemBySkuIdInput,
ApplyCheckoutSessionDeliveryMethodInput,
ApplyCheckoutSessionDiscountInput,
ApplyCheckoutSessionFulfillmentLocationInput,
ApplyCheckoutSessionShippingMethodInput,
+ ApplyDiscountCodesInput,
CheckoutSession,
CheckoutSessionInput,
ConfirmCheckoutMutationInput,
+ DeleteLineItemByIdInput,
DraftOrderPriceAdjustmentsQueryInput,
GetCheckoutSessionShippingRatesInput,
GetCheckoutSessionTaxesInput,
@@ -22,7 +35,9 @@ import type {
SkuGroupInput,
SkuGroupsInput,
SkuInput,
+ UpdateCartOrderInput,
UpdateDraftOrderInput,
+ UpdateLineItemByIdInput,
} from '@/types';
import {
ApplyCheckoutSessionDeliveryMethodMutation,
@@ -48,9 +63,13 @@ import {
GetCheckoutSessionQuery,
} from './checkout-queries.ts';
-function getHostByEnvironment(apiHost?: string, service = 'checkout'): string {
+function getHostByEnvironment(apiHost?: string): string {
// Use provided apiHost, otherwise default to production
- return `https://${service}.commerce.${apiHost || 'api.godaddy.com'}`;
+ return `https://checkout.commerce.${apiHost || 'api.godaddy.com'}`;
+}
+
+function getApiHostByEnvironment(apiHost?: string, endpoint?: string): string {
+ return `https://${apiHost || 'api.godaddy.com'}${endpoint ? endpoint : ''}`;
}
// Type for createCheckoutSession input with kebab-case appearance
@@ -1066,10 +1085,13 @@ export function getSkuGroups(
clientId: string,
apiHost?: string
) {
- const GODADDY_HOST = getHostByEnvironment(apiHost, 'catalog');
+ const GODADDY_HOST = getApiHostByEnvironment(
+ apiHost,
+ `/v2/commerce/stores/${storeId}/catalog-subgraph/storefront`
+ );
return graphqlRequestWithErrors>(
- `${GODADDY_HOST}/storefront`,
+ GODADDY_HOST,
SkuGroupsQuery,
input,
{
@@ -1085,10 +1107,13 @@ export function getSkuGroup(
clientId: string,
apiHost?: string
) {
- const GODADDY_HOST = getHostByEnvironment(apiHost, 'catalog');
+ const GODADDY_HOST = getApiHostByEnvironment(
+ apiHost,
+ `/v2/commerce/stores/${storeId}/catalog-subgraph/storefront`
+ );
return graphqlRequestWithErrors>(
- `${GODADDY_HOST}/storefront`,
+ GODADDY_HOST,
SkuGroupQuery,
input,
{
@@ -1104,10 +1129,13 @@ export function getSku(
clientId: string,
apiHost?: string
) {
- const GODADDY_HOST = getHostByEnvironment(apiHost, 'catalog');
+ const GODADDY_HOST = getApiHostByEnvironment(
+ apiHost,
+ `/v2/commerce/stores/${storeId}/catalog-subgraph/storefront`
+ );
return graphqlRequestWithErrors>(
- `${GODADDY_HOST}/storefront`,
+ GODADDY_HOST,
SkuQuery,
input,
{
@@ -1116,3 +1144,157 @@ export function getSku(
}
);
}
+
+// Cart/Orders functions
+export function createCartOrder(
+ input: AddCartOrderInput,
+ storeId: string,
+ clientId: string,
+ apiHost?: string
+) {
+ const GODADDY_HOST = getApiHostByEnvironment(
+ apiHost,
+ `/v1/commerce/order-storefront-subgraph`
+ );
+
+ return graphqlRequestWithErrors>(
+ GODADDY_HOST,
+ AddCartOrderMutation,
+ { input },
+ {
+ 'X-Store-ID': storeId,
+ 'X-Client-ID': clientId,
+ }
+ );
+}
+
+export function getCartOrder(
+ orderId: string,
+ storeId: string,
+ clientId: string,
+ apiHost?: string
+) {
+ const GODADDY_HOST = getApiHostByEnvironment(
+ apiHost,
+ `/v1/commerce/order-storefront-subgraph`
+ );
+
+ return graphqlRequestWithErrors>(
+ GODADDY_HOST,
+ GetCartOrderQuery,
+ { id: orderId },
+ {
+ 'X-Store-ID': storeId,
+ 'X-Client-ID': clientId,
+ }
+ );
+}
+
+export function addCartLineItem(
+ input: AddLineItemBySkuIdInput,
+ storeId: string,
+ clientId: string,
+ apiHost?: string
+) {
+ const GODADDY_HOST = getApiHostByEnvironment(
+ apiHost,
+ `/v1/commerce/order-storefront-subgraph`
+ );
+
+ return graphqlRequestWithErrors>(
+ GODADDY_HOST,
+ AddLineItemBySkuIdMutation,
+ { input },
+ {
+ 'X-Store-ID': storeId,
+ 'X-Client-ID': clientId,
+ }
+ );
+}
+
+export function updateCartOrder(
+ input: UpdateCartOrderInput,
+ storeId: string,
+ clientId: string,
+ apiHost?: string
+) {
+ const GODADDY_HOST = getApiHostByEnvironment(
+ apiHost,
+ `/v1/commerce/order-storefront-subgraph`
+ );
+
+ return graphqlRequestWithErrors>(
+ GODADDY_HOST,
+ UpdateCartOrderMutation,
+ { input },
+ {
+ 'X-Store-ID': storeId,
+ 'X-Client-ID': clientId,
+ }
+ );
+}
+
+export function deleteCartLineItem(
+ input: DeleteLineItemByIdInput,
+ storeId: string,
+ clientId: string,
+ apiHost?: string
+) {
+ const GODADDY_HOST = getApiHostByEnvironment(
+ apiHost,
+ `/v1/commerce/order-storefront-subgraph`
+ );
+
+ return graphqlRequestWithErrors>(
+ GODADDY_HOST,
+ DeleteLineItemByIdMutation,
+ input,
+ {
+ 'X-Store-ID': storeId,
+ 'X-Client-ID': clientId,
+ }
+ );
+}
+
+export function applyCartDiscountCodes(
+ input: ApplyDiscountCodesInput,
+ storeId: string,
+ clientId: string,
+ apiHost?: string
+) {
+ const GODADDY_HOST = getApiHostByEnvironment(
+ apiHost,
+ `/v1/commerce/order-storefront-subgraph`
+ );
+ return graphqlRequestWithErrors>(
+ GODADDY_HOST,
+ ApplyDiscountCodesMutation,
+ { input },
+ {
+ 'X-Store-ID': storeId,
+ 'X-Client-ID': clientId,
+ }
+ );
+}
+
+export function updateCartLineItem(
+ input: UpdateLineItemByIdInput,
+ storeId: string,
+ clientId: string,
+ apiHost?: string
+) {
+ const GODADDY_HOST = getApiHostByEnvironment(
+ apiHost,
+ `/v1/commerce/order-storefront-subgraph`
+ );
+
+ return graphqlRequestWithErrors>(
+ GODADDY_HOST,
+ UpdateLineItemByIdMutation,
+ { input },
+ {
+ 'X-Store-ID': storeId,
+ 'X-Client-ID': clientId,
+ }
+ );
+}
diff --git a/packages/react/src/lib/godaddy/orders-storefront-env.ts b/packages/react/src/lib/godaddy/orders-storefront-env.ts
index 3949dcaf..c35d6566 100644
--- a/packages/react/src/lib/godaddy/orders-storefront-env.ts
+++ b/packages/react/src/lib/godaddy/orders-storefront-env.ts
@@ -64,11 +64,8 @@ const introspection = {
{
name: 'totals',
type: {
- kind: 'NON_NULL',
- ofType: {
- kind: 'INPUT_OBJECT',
- name: 'OrderTotalsInput',
- },
+ kind: 'INPUT_OBJECT',
+ name: 'OrderTotalsInput',
},
},
{
@@ -1326,6 +1323,15 @@ const introspection = {
args: [],
isDeprecated: false,
},
+ {
+ name: 'unitAmount',
+ type: {
+ kind: 'OBJECT',
+ name: 'Money',
+ },
+ args: [],
+ isDeprecated: false,
+ },
{
name: 'updatedAt',
type: {
diff --git a/packages/react/src/lib/godaddy/orders-storefront-mutations.ts b/packages/react/src/lib/godaddy/orders-storefront-mutations.ts
new file mode 100644
index 00000000..f485ce0f
--- /dev/null
+++ b/packages/react/src/lib/godaddy/orders-storefront-mutations.ts
@@ -0,0 +1,302 @@
+import { graphql } from '@/lib/gql/gql-orders-storefront.tada';
+
+export const AddCartOrderMutation = graphql(`
+ mutation AddCartOrder($input: AddDraftOrderInput!) {
+ addDraftOrder(input: $input) {
+ id
+ customerId
+ createdAt
+ updatedAt
+ context {
+ storeId
+ channelId
+ }
+ lineItems {
+ id
+ name
+ quantity
+ skuId
+ type
+ fulfillmentMode
+ totals {
+ subTotal {
+ value
+ currencyCode
+ }
+ taxTotal {
+ value
+ currencyCode
+ }
+ discountTotal {
+ value
+ currencyCode
+ }
+ feeTotal {
+ value
+ currencyCode
+ }
+ }
+ }
+ totals {
+ subTotal {
+ value
+ currencyCode
+ }
+ shippingTotal {
+ value
+ currencyCode
+ }
+ taxTotal {
+ value
+ currencyCode
+ }
+ discountTotal {
+ value
+ currencyCode
+ }
+ feeTotal {
+ value
+ currencyCode
+ }
+ total {
+ value
+ currencyCode
+ }
+ }
+ }
+ }
+`);
+
+export const AddLineItemBySkuIdMutation = graphql(`
+ mutation AddLineItemBySkuId($input: AddLineItemInput!) {
+ addLineItemBySkuId(input: $input) {
+ id
+ name
+ quantity
+ skuId
+ type
+ fulfillmentMode
+ details {
+ productAssetUrl
+ sku
+ unitOfMeasure
+ selectedOptions {
+ attribute
+ values
+ }
+ selectedAddons {
+ attribute
+ sku
+ values {
+ name
+ costAdjustment {
+ value
+ currencyCode
+ }
+ }
+ }
+ }
+ totals {
+ subTotal {
+ value
+ currencyCode
+ }
+ taxTotal {
+ value
+ currencyCode
+ }
+ discountTotal {
+ value
+ currencyCode
+ }
+ feeTotal {
+ value
+ currencyCode
+ }
+ }
+ discounts {
+ id
+ name
+ code
+ amount {
+ value
+ currencyCode
+ }
+ }
+ taxes {
+ id
+ name
+ amount {
+ value
+ currencyCode
+ }
+ ratePercentage
+ }
+ createdAt
+ updatedAt
+ }
+ }
+`);
+
+export const UpdateCartOrderMutation = graphql(`
+ mutation UpdateCartOrder($input: UpdateDraftOrderInput!) {
+ updateDraftOrder(input: $input) {
+ id
+ customerId
+ createdAt
+ updatedAt
+ context {
+ storeId
+ channelId
+ }
+ lineItems {
+ id
+ name
+ quantity
+ skuId
+ type
+ fulfillmentMode
+ totals {
+ subTotal {
+ value
+ currencyCode
+ }
+ taxTotal {
+ value
+ currencyCode
+ }
+ discountTotal {
+ value
+ currencyCode
+ }
+ feeTotal {
+ value
+ currencyCode
+ }
+ }
+ }
+ totals {
+ subTotal {
+ value
+ currencyCode
+ }
+ shippingTotal {
+ value
+ currencyCode
+ }
+ taxTotal {
+ value
+ currencyCode
+ }
+ discountTotal {
+ value
+ currencyCode
+ }
+ feeTotal {
+ value
+ currencyCode
+ }
+ total {
+ value
+ currencyCode
+ }
+ }
+ }
+ }
+`);
+
+export const DeleteLineItemByIdMutation = graphql(`
+ mutation DeleteLineItemById($id: ID!, $orderId: ID!) {
+ deleteLineItemById(id: $id, orderId: $orderId)
+ }
+`);
+
+export const ApplyDiscountCodesMutation = graphql(`
+ mutation ApplyDiscountCodes($input: ApplyDiscountCodesInput!) {
+ applyDiscountCodes(input: $input) {
+ id
+ discounts {
+ id
+ name
+ code
+ amount {
+ value
+ currencyCode
+ }
+ ratePercentage
+ appliedBeforeTax
+ }
+ totals {
+ subTotal {
+ value
+ currencyCode
+ }
+ shippingTotal {
+ value
+ currencyCode
+ }
+ taxTotal {
+ value
+ currencyCode
+ }
+ discountTotal {
+ value
+ currencyCode
+ }
+ productDiscountTotal {
+ value
+ currencyCode
+ }
+ shippingDiscountTotal {
+ value
+ currencyCode
+ }
+ feeTotal {
+ value
+ currencyCode
+ }
+ total {
+ value
+ currencyCode
+ }
+ }
+ }
+ }
+`);
+
+export const UpdateLineItemByIdMutation = graphql(`
+ mutation UpdateLineItemById($input: UpdateLineItemByIdInput!) {
+ updateLineItemById(input: $input) {
+ id
+ name
+ quantity
+ skuId
+ type
+ fulfillmentMode
+ details {
+ productAssetUrl
+ sku
+ unitOfMeasure
+ }
+ totals {
+ subTotal {
+ value
+ currencyCode
+ }
+ taxTotal {
+ value
+ currencyCode
+ }
+ discountTotal {
+ value
+ currencyCode
+ }
+ feeTotal {
+ value
+ currencyCode
+ }
+ }
+ updatedAt
+ }
+ }
+`);
diff --git a/packages/react/src/lib/godaddy/orders-storefront-queries.ts b/packages/react/src/lib/godaddy/orders-storefront-queries.ts
new file mode 100644
index 00000000..4a4dcf4c
--- /dev/null
+++ b/packages/react/src/lib/godaddy/orders-storefront-queries.ts
@@ -0,0 +1,169 @@
+import { graphql } from '@/lib/gql/gql-orders-storefront.tada';
+
+export const GetCartOrderQuery = graphql(`
+ query GetCartOrder($id: ID!) {
+ orderById(id: $id) {
+ id
+ customerId
+ createdAt
+ updatedAt
+ context {
+ storeId
+ channelId
+ }
+ lineItems {
+ id
+ name
+ quantity
+ skuId
+ type
+ fulfillmentMode
+ details {
+ productAssetUrl
+ sku
+ unitOfMeasure
+ selectedOptions {
+ attribute
+ values
+ }
+ selectedAddons {
+ attribute
+ sku
+ values {
+ name
+ costAdjustment {
+ value
+ currencyCode
+ }
+ }
+ }
+ }
+ totals {
+ subTotal {
+ value
+ currencyCode
+ }
+ taxTotal {
+ value
+ currencyCode
+ }
+ discountTotal {
+ value
+ currencyCode
+ }
+ feeTotal {
+ value
+ currencyCode
+ }
+ }
+ discounts {
+ id
+ name
+ code
+ amount {
+ value
+ currencyCode
+ }
+ ratePercentage
+ }
+ taxes {
+ id
+ name
+ amount {
+ value
+ currencyCode
+ }
+ ratePercentage
+ }
+ notes {
+ id
+ content
+ author
+ authorType
+ }
+ }
+ totals {
+ subTotal {
+ value
+ currencyCode
+ }
+ shippingTotal {
+ value
+ currencyCode
+ }
+ taxTotal {
+ value
+ currencyCode
+ }
+ discountTotal {
+ value
+ currencyCode
+ }
+ productDiscountTotal {
+ value
+ currencyCode
+ }
+ shippingDiscountTotal {
+ value
+ currencyCode
+ }
+ feeTotal {
+ value
+ currencyCode
+ }
+ total {
+ value
+ currencyCode
+ }
+ }
+ discounts {
+ id
+ name
+ code
+ amount {
+ value
+ currencyCode
+ }
+ ratePercentage
+ appliedBeforeTax
+ }
+ taxes {
+ id
+ name
+ amount {
+ value
+ currencyCode
+ }
+ ratePercentage
+ included
+ exempted
+ }
+ shipping {
+ firstName
+ lastName
+ email
+ phone
+ companyName
+ address {
+ addressLine1
+ addressLine2
+ addressLine3
+ adminArea1
+ adminArea2
+ adminArea3
+ adminArea4
+ postalCode
+ countryCode
+ }
+ }
+ notes {
+ id
+ content
+ author
+ authorType
+ createdAt
+ }
+ tags
+ }
+ }
+`);
diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts
index 7f78bda9..d4b06da6 100644
--- a/packages/react/src/types.ts
+++ b/packages/react/src/types.ts
@@ -23,6 +23,15 @@ import {
DraftOrderSkusQuery,
DraftOrderTaxesQuery,
} from '@/lib/godaddy/checkout-queries.ts';
+import {
+ AddCartOrderMutation,
+ AddLineItemBySkuIdMutation,
+ ApplyDiscountCodesMutation,
+ DeleteLineItemByIdMutation,
+ UpdateCartOrderMutation,
+ UpdateLineItemByIdMutation,
+} from '@/lib/godaddy/orders-storefront-mutations.ts';
+import { GetCartOrderQuery } from '@/lib/godaddy/orders-storefront-queries.ts';
export const PaymentProvider = {
STRIPE: 'stripe',
@@ -270,3 +279,68 @@ export type SKUAttributeValue = NonNullable<
>['edges']
>[number]
>['node'];
+
+// Cart/Orders types
+export type AddCartOrderInput = VariablesOf<
+ typeof AddCartOrderMutation
+>['input'];
+
+export type AddLineItemBySkuIdInput = VariablesOf<
+ typeof AddLineItemBySkuIdMutation
+>['input'];
+
+export type UpdateCartOrderInput = VariablesOf<
+ typeof UpdateCartOrderMutation
+>['input'];
+
+export type UpdateLineItemByIdInput = VariablesOf<
+ typeof UpdateLineItemByIdMutation
+>['input'];
+
+export type DeleteLineItemByIdInput = VariablesOf<
+ typeof DeleteLineItemByIdMutation
+>;
+
+export type ApplyDiscountCodesInput = VariablesOf<
+ typeof ApplyDiscountCodesMutation
+>['input'];
+
+export type CartOrder = NonNullable<
+ ResultOf['orderById']
+>;
+
+export type CartLineItem = NonNullable<
+ NonNullable['orderById']>['lineItems']
+>[number];
+
+export type CartOrderTotals = NonNullable<
+ NonNullable['orderById']>['totals']
+>;
+
+export type CartLineItemTotals = NonNullable<
+ NonNullable<
+ NonNullable['orderById']>['lineItems']
+ >[number]['totals']
+>;
+
+export type CartDiscount = NonNullable<
+ NonNullable['orderById']>['discounts']
+>[number];
+
+export type CartTax = NonNullable<
+ NonNullable['orderById']>['taxes']
+>[number];
+
+export type CartShippingInfo = NonNullable<
+ NonNullable['orderById']>['shipping']
+>;
+
+export type CartNote = NonNullable<
+ NonNullable['orderById']>['notes']
+>[number];
+
+export type CartLineItemDetails = NonNullable<
+ NonNullable<
+ NonNullable['orderById']>['lineItems']
+ >[number]['details']
+>;