Changes
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
import { toast } from "sonner";
|
||||
|
||||
// Shopify API Configuration
|
||||
const SHOPIFY_API_VERSION = '2025-07';
|
||||
const SHOPIFY_STORE_PERMANENT_DOMAIN = 'sweet-office-treats-2ar62.myshopify.com';
|
||||
const SHOPIFY_STOREFRONT_URL = `https://${SHOPIFY_STORE_PERMANENT_DOMAIN}/api/${SHOPIFY_API_VERSION}/graphql.json`;
|
||||
const SHOPIFY_STOREFRONT_TOKEN = 'd9c2c43fc30d96d1742cc76da58c4ddb';
|
||||
|
||||
export interface ShopifyProduct {
|
||||
node: {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
handle: string;
|
||||
priceRange: {
|
||||
minVariantPrice: {
|
||||
amount: string;
|
||||
currencyCode: string;
|
||||
};
|
||||
};
|
||||
images: {
|
||||
edges: Array<{
|
||||
node: {
|
||||
url: string;
|
||||
altText: string | null;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
variants: {
|
||||
edges: Array<{
|
||||
node: {
|
||||
id: string;
|
||||
title: string;
|
||||
price: {
|
||||
amount: string;
|
||||
currencyCode: string;
|
||||
};
|
||||
availableForSale: boolean;
|
||||
selectedOptions: Array<{
|
||||
name: string;
|
||||
value: string;
|
||||
}>;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
options: Array<{
|
||||
name: string;
|
||||
values: string[];
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
// Storefront API helper function
|
||||
export async function storefrontApiRequest(query: string, variables: Record<string, unknown> = {}) {
|
||||
const response = await fetch(SHOPIFY_STOREFRONT_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Shopify-Storefront-Access-Token': SHOPIFY_STOREFRONT_TOKEN
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
variables,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.status === 402) {
|
||||
toast.error("Shopify: Betalning krävs", {
|
||||
description: "Shopify API kräver en aktiv betalplan. Besök admin.shopify.com för att uppgradera.",
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.errors) {
|
||||
throw new Error(`Error calling Shopify: ${data.errors.map((e: { message: string }) => e.message).join(', ')}`);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Fetch products query
|
||||
export const STOREFRONT_PRODUCTS_QUERY = `
|
||||
query GetProducts($first: Int!, $query: String) {
|
||||
products(first: $first, query: $query) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
description
|
||||
handle
|
||||
priceRange {
|
||||
minVariantPrice {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
}
|
||||
images(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
url
|
||||
altText
|
||||
}
|
||||
}
|
||||
}
|
||||
variants(first: 10) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
price {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
availableForSale
|
||||
selectedOptions {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
options {
|
||||
name
|
||||
values
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Cart mutations
|
||||
export const CART_QUERY = `
|
||||
query cart($id: ID!) {
|
||||
cart(id: $id) { id totalQuantity }
|
||||
}
|
||||
`;
|
||||
|
||||
export const CART_CREATE_MUTATION = `
|
||||
mutation cartCreate($input: CartInput!) {
|
||||
cartCreate(input: $input) {
|
||||
cart {
|
||||
id
|
||||
checkoutUrl
|
||||
lines(first: 100) { edges { node { id merchandise { ... on ProductVariant { id } } } } }
|
||||
}
|
||||
userErrors { field message }
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const CART_LINES_ADD_MUTATION = `
|
||||
mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
|
||||
cartLinesAdd(cartId: $cartId, lines: $lines) {
|
||||
cart {
|
||||
id
|
||||
lines(first: 100) { edges { node { id merchandise { ... on ProductVariant { id } } } } }
|
||||
}
|
||||
userErrors { field message }
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const CART_LINES_UPDATE_MUTATION = `
|
||||
mutation cartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
|
||||
cartLinesUpdate(cartId: $cartId, lines: $lines) {
|
||||
cart { id }
|
||||
userErrors { field message }
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const CART_LINES_REMOVE_MUTATION = `
|
||||
mutation cartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
|
||||
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
||||
cart { id }
|
||||
userErrors { field message }
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function formatCheckoutUrl(checkoutUrl: string): string {
|
||||
try {
|
||||
const url = new URL(checkoutUrl);
|
||||
url.searchParams.set('channel', 'online_store');
|
||||
return url.toString();
|
||||
} catch {
|
||||
return checkoutUrl;
|
||||
}
|
||||
}
|
||||
|
||||
export function isCartNotFoundError(userErrors: Array<{ field: string[] | null; message: string }>): boolean {
|
||||
return userErrors.some(e =>
|
||||
e.message.toLowerCase().includes('cart not found') ||
|
||||
e.message.toLowerCase().includes('does not exist')
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user