diff --git a/EOF b/EOF new file mode 100644 index 0000000..e69de29 diff --git a/features.md b/features.md new file mode 100644 index 0000000..0ea114f --- /dev/null +++ b/features.md @@ -0,0 +1,3 @@ +programmatic seo +pop up and exit pop to grow emaillist connected with resend and mautic. want to always have my list growing and owned by me on my server +abandoned cart setup with sequences to get people back diff --git a/public/debug-op.js b/public/debug-op.js new file mode 100644 index 0000000..2138ee1 --- /dev/null +++ b/public/debug-op.js @@ -0,0 +1,40 @@ +// OpenPanel Debug Script +// Run this in browser console to test OpenPanel + +(function debugOpenPanel() { + console.log('=== OpenPanel Debug ==='); + + // Check if OpenPanel is loaded + if (typeof window.op === 'undefined') { + console.error('โŒ OpenPanel SDK not loaded (window.op is undefined)'); + console.log('Script URL should be:', 'https://op.nodecrew.me/op1.js'); + return; + } + + console.log('โœ… OpenPanel SDK loaded'); + console.log('window.op:', window.op); + + // Check client ID + const clientId = window.op._clientId || 'not set'; + console.log('Client ID:', clientId); + + // Try to track an event + console.log('Attempting to track test event...'); + window.op.track('debug_test', { source: 'console', timestamp: new Date().toISOString() }) + .then(() => console.log('โœ… Track successful')) + .catch(err => console.error('โŒ Track failed:', err)); + + // Check network requests + console.log(''); + console.log('Check Network tab for requests to:'); + console.log('- https://manoonoils.com/api/op/track'); + console.log('- https://op.nodecrew.me/api/track'); + + // Common issues + console.log(''); + console.log('Common issues:'); + console.log('1. Ad blockers (try disabling uBlock/AdBlock)'); + console.log('2. CORS errors (check console for red errors)'); + console.log('3. Do Not Track enabled in browser'); + console.log('4. Private/Incognito mode (some blockers active)'); +})(); diff --git a/scripts/test-checkout-shipping.js b/scripts/test-checkout-shipping.js new file mode 100644 index 0000000..b974c59 --- /dev/null +++ b/scripts/test-checkout-shipping.js @@ -0,0 +1,310 @@ +#!/usr/bin/env node +/** + * Test script for checkout shipping cost calculation + * Creates a checkout via API and verifies totalPrice includes shipping + */ + +const SALEOR_API_URL = process.env.NEXT_PUBLIC_SALEOR_API_URL || 'https://api.manoonoils.com/graphql/'; + +// Test data +const TEST_VARIANT_ID = 'UHJvZHVjdFZhcmlhbnQ6Mjk0'; // Replace with actual variant ID +const TEST_EMAIL = 'test@example.com'; + +const TEST_SHIPPING_ADDRESS = { + firstName: 'Test', + lastName: 'User', + streetAddress1: '123 Test Street', + city: 'Belgrade', + postalCode: '11000', + country: 'RS', + phone: '+38160123456' +}; + +async function saleorFetch(query, variables = {}, token = null) { + const headers = { + 'Content-Type': 'application/json', + }; + + if (token) { + headers['Authorization'] = `JWT ${token}`; + } + + const response = await fetch(SALEOR_API_URL, { + method: 'POST', + headers, + body: JSON.stringify({ query, variables }), + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + + if (result.errors) { + throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`); + } + + return result.data; +} + +async function testCheckoutWithShipping() { + console.log('๐Ÿงช Testing checkout shipping cost calculation...\n'); + + try { + // Step 1: Create checkout + console.log('Step 1: Creating checkout...'); + const checkoutCreateMutation = ` + mutation CheckoutCreate($input: CheckoutCreateInput!) { + checkoutCreate(input: $input) { + checkout { + id + token + totalPrice { + gross { + amount + currency + } + } + subtotalPrice { + gross { + amount + currency + } + } + } + errors { + field + message + } + } + } + `; + + const checkoutResult = await saleorFetch(checkoutCreateMutation, { + input: { + channel: 'default-channel', + email: TEST_EMAIL, + lines: [], + languageCode: 'SR' + } + }); + + if (checkoutResult.checkoutCreate.errors?.length > 0) { + throw new Error(`Checkout creation failed: ${checkoutResult.checkoutCreate.errors[0].message}`); + } + + const checkout = checkoutResult.checkoutCreate.checkout; + console.log(`โœ… Checkout created: ${checkout.id}`); + console.log(` Token: ${checkout.token}`); + console.log(` Initial total: ${checkout.totalPrice.gross.amount} ${checkout.totalPrice.gross.currency}\n`); + + // Step 2: Add product to checkout + console.log('Step 2: Adding product to checkout...'); + const linesAddMutation = ` + mutation CheckoutLinesAdd($checkoutId: ID!, $lines: [CheckoutLineInput!]!) { + checkoutLinesAdd(checkoutId: $checkoutId, lines: $lines) { + checkout { + id + totalPrice { + gross { + amount + currency + } + } + subtotalPrice { + gross { + amount + currency + } + } + lines { + id + quantity + totalPrice { + gross { + amount + } + } + } + } + errors { + field + message + } + } + } + `; + + // First, let's query for available products to get a real variant ID + console.log(' Querying available products...'); + const productsQuery = ` + query Products { + products(channel: "default-channel", first: 1) { + edges { + node { + id + name + variants { + id + name + } + } + } + } + } + `; + + const productsResult = await saleorFetch(productsQuery); + const product = productsResult.products.edges[0]?.node; + + if (!product || !product.variants?.[0]) { + throw new Error('No products found in store'); + } + + const variantId = product.variants[0].id; + console.log(` Product: ${product.name}, Variant: ${product.variants[0].name}`); + + const linesResult = await saleorFetch(linesAddMutation, { + checkoutId: checkout.id, + lines: [{ variantId, quantity: 1 }] + }); + + if (linesResult.checkoutLinesAdd.errors?.length > 0) { + throw new Error(`Adding lines failed: ${linesResult.checkoutLinesAdd.errors[0].message}`); + } + + const checkoutWithLines = linesResult.checkoutLinesAdd.checkout; + const productTotal = checkoutWithLines.totalPrice.gross.amount; + console.log(`โœ… Product added (qty: 1)`); + console.log(` Product total: ${productTotal} RSD\n`); + + // Step 3: Set shipping address + console.log('Step 3: Setting shipping address...'); + const shippingAddressMutation = ` + mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: AddressInput!) { + checkoutShippingAddressUpdate(checkoutId: $checkoutId, shippingAddress: $shippingAddress) { + checkout { + id + shippingMethods { + id + name + price { + amount + currency + } + } + } + errors { + field + message + } + } + } + `; + + const shippingResult = await saleorFetch(shippingAddressMutation, { + checkoutId: checkout.id, + shippingAddress: TEST_SHIPPING_ADDRESS + }); + + if (shippingResult.checkoutShippingAddressUpdate.errors?.length > 0) { + throw new Error(`Setting shipping address failed: ${shippingResult.checkoutShippingAddressUpdate.errors[0].message}`); + } + + const availableMethods = shippingResult.checkoutShippingAddressUpdate.checkout.shippingMethods; + console.log(`โœ… Shipping address set`); + console.log(` Available shipping methods: ${availableMethods.length}`); + + if (availableMethods.length === 0) { + console.log(' โš ๏ธ No shipping methods available for this address/region'); + return; + } + + availableMethods.forEach((method, i) => { + console.log(` [${i + 1}] ${method.name}: ${method.price.amount} ${method.price.currency}`); + }); + console.log(''); + + // Step 4: Set shipping method + const selectedMethod = availableMethods[0]; + console.log(`Step 4: Selecting shipping method: ${selectedMethod.name} (${selectedMethod.price.amount} RSD)...`); + + const shippingMethodMutation = ` + mutation CheckoutShippingMethodUpdate($checkoutId: ID!, $shippingMethodId: ID!) { + checkoutShippingMethodUpdate(checkoutId: $checkoutId, shippingMethodId: $shippingMethodId) { + checkout { + id + totalPrice { + gross { + amount + currency + } + } + subtotalPrice { + gross { + amount + currency + } + } + shippingPrice { + gross { + amount + currency + } + } + } + errors { + field + message + } + } + } + `; + + const methodResult = await saleorFetch(shippingMethodMutation, { + checkoutId: checkout.id, + shippingMethodId: selectedMethod.id + }); + + if (methodResult.checkoutShippingMethodUpdate.errors?.length > 0) { + throw new Error(`Setting shipping method failed: ${methodResult.checkoutShippingMethodUpdate.errors[0].message}`); + } + + const finalCheckout = methodResult.checkoutShippingMethodUpdate.checkout; + const subtotal = finalCheckout.subtotalPrice.gross.amount; + const shipping = finalCheckout.shippingPrice.gross.amount; + const finalTotal = finalCheckout.totalPrice.gross.amount; + const expectedTotal = subtotal + shipping; + + console.log(`โœ… Shipping method set`); + console.log(` Subtotal: ${subtotal} RSD`); + console.log(` Shipping: ${shipping} RSD`); + console.log(` Total: ${finalTotal} RSD`); + console.log(` Expected: ${expectedTotal} RSD`); + console.log(''); + + // Verification + console.log('๐Ÿ“Š VERIFICATION:'); + if (finalTotal === expectedTotal) { + console.log('โœ… PASS: Total includes shipping cost correctly'); + console.log(` ${subtotal} + ${shipping} = ${finalTotal}`); + } else { + console.log('โŒ FAIL: Total does NOT include shipping cost'); + console.log(` Expected: ${expectedTotal}, Got: ${finalTotal}`); + console.log(` Difference: ${expectedTotal - finalTotal}`); + } + + // Cleanup - delete checkout + console.log('\n๐Ÿงน Cleaning up test checkout...'); + // Note: Checkout deletion requires admin permissions + console.log(` Checkout ID for manual cleanup: ${checkout.id}`); + + } catch (error) { + console.error('\nโŒ Test failed:', error.message); + process.exit(1); + } +} + +// Run the test +testCheckoutWithShipping(); diff --git a/scripts/test-frontend-checkout.js b/scripts/test-frontend-checkout.js new file mode 100644 index 0000000..e69de29 diff --git a/scripts/test-frontend.mjs b/scripts/test-frontend.mjs new file mode 100644 index 0000000..cce9a0d --- /dev/null +++ b/scripts/test-frontend.mjs @@ -0,0 +1,137 @@ +const SALEOR_API_URL = 'https://api.manoonoils.com/graphql/'; + +async function saleorFetch(query, variables = {}) { + const response = await fetch(SALEOR_API_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ query, variables }), + }); + const result = await response.json(); + if (result.errors) { + console.error('GraphQL Errors:', JSON.stringify(result.errors, null, 2)); + throw new Error(JSON.stringify(result.errors)); + } + return result.data; +} + +async function test() { + // Create checkout + const createResult = await saleorFetch(` + mutation { + checkoutCreate(input: { + channel: "default-channel" + email: "test@test.com" + lines: [{ variantId: "UHJvZHVjdFZhcmlhbnQ6Mjk0", quantity: 1 }] + languageCode: SR + }) { + checkout { + id + token + totalPrice { gross { amount } } + subtotalPrice { gross { amount } } + } + errors { + field + message + code + } + } + } + `); + + if (createResult.checkoutCreate.errors?.length > 0) { + console.error('Checkout creation errors:', createResult.checkoutCreate.errors); + throw new Error('Checkout creation failed'); + } + if (!createResult.checkoutCreate.checkout) { + console.error('Create result:', createResult); + throw new Error('Checkout creation returned null'); + } + const checkout = createResult.checkoutCreate.checkout; + const token = checkout.token; + + console.log('Created checkout:'); + console.log(' ID:', checkout.id); + console.log(' Token:', token); + console.log(' Initial Total:', checkout.totalPrice.gross.amount); + + // Set address + await saleorFetch(` + mutation { + checkoutShippingAddressUpdate( + checkoutId: "${checkout.id}" + shippingAddress: { + firstName: "Test" + lastName: "User" + streetAddress1: "123 Street" + city: "Belgrade" + postalCode: "11000" + country: "RS" + phone: "+38160123456" + } + ) { + checkout { + shippingMethods { id name price { amount } } + } + } + } + `); + + // Query by token (what refreshCheckout does) + const tokenQuery = await saleorFetch(` + query { + checkout(token: "${token}") { + id + token + totalPrice { gross { amount } } + subtotalPrice { gross { amount } } + shippingPrice { gross { amount } } + shippingMethods { id name price { amount } } + } + } + `); + + console.log('\nQuery by token (before shipping method):'); + console.log(' Total:', tokenQuery.checkout.totalPrice.gross.amount); + console.log(' Subtotal:', tokenQuery.checkout.subtotalPrice.gross.amount); + console.log(' Shipping:', tokenQuery.checkout.shippingPrice.gross.amount); + console.log(' Methods:', tokenQuery.checkout.shippingMethods.length); + + if (tokenQuery.checkout.shippingMethods.length > 0) { + const methodId = tokenQuery.checkout.shippingMethods[0].id; + + // Set shipping method + await saleorFetch(` + mutation { + checkoutShippingMethodUpdate( + checkoutId: "${checkout.id}" + shippingMethodId: "${methodId}" + ) { + checkout { + totalPrice { gross { amount } } + subtotalPrice { gross { amount } } + shippingPrice { gross { amount } } + } + } + } + `); + + // Query by token again (what should happen after refreshCheckout) + const afterMethod = await saleorFetch(` + query { + checkout(token: "${token}") { + totalPrice { gross { amount } } + subtotalPrice { gross { amount } } + shippingPrice { gross { amount } } + } + } + `); + + console.log('\nQuery by token (AFTER shipping method):'); + console.log(' Total:', afterMethod.checkout.totalPrice.gross.amount); + console.log(' Subtotal:', afterMethod.checkout.subtotalPrice.gross.amount); + console.log(' Shipping:', afterMethod.checkout.shippingPrice.gross.amount); + } +} + +test().catch(console.error); diff --git a/scripts/test-full-checkout-flow.js b/scripts/test-full-checkout-flow.js new file mode 100644 index 0000000..5f81216 --- /dev/null +++ b/scripts/test-full-checkout-flow.js @@ -0,0 +1,254 @@ +#!/usr/bin/env node +/** + * Complete API test simulating frontend checkout flow + * Tests every step the frontend takes + */ + +const SALEOR_API_URL = 'https://api.manoonoils.com/graphql/'; + +async function saleorFetch(query, variables = {}) { + const response = await fetch(SALEOR_API_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ query: query.replace(/\n\s*/g, ' '), variables }), + }); + const result = await response.json(); + if (result.errors) { + console.error('GraphQL Error:', JSON.stringify(result.errors, null, 2)); + throw new Error(result.errors[0].message); + } + return result.data; +} + +async function runTest() { + console.log('๐Ÿงช TESTING FRONTEND CHECKOUT FLOW\n'); + console.log('=' .repeat(50)); + + let checkoutId = null; + let checkoutToken = null; + let shippingMethodId = null; + + try { + // STEP 1: Create checkout (like frontend does on first cart add) + console.log('\n๐Ÿ“ฆ STEP 1: Create Checkout'); + console.log('-'.repeat(50)); + + const createResult = await saleorFetch(` + mutation CheckoutCreate($input: CheckoutCreateInput!) { + checkoutCreate(input: $input) { + checkout { + id + token + totalPrice { gross { amount currency } } + subtotalPrice { gross { amount } } + } + errors { field message } + } + } + `, { + input: { + channel: "default-channel", + email: "test@test.com", + lines: [], + languageCode: "SR" + } + }); + + checkoutId = createResult.checkoutCreate.checkout.id; + checkoutToken = createResult.checkoutCreate.checkout.token; + + console.log('โœ… Checkout created'); + console.log(' ID:', checkoutId); + console.log(' Token:', checkoutToken); + console.log(' Initial Total:', createResult.checkoutCreate.checkout.totalPrice.gross.amount, 'RSD'); + + // STEP 2: Add product (like frontend does) + console.log('\n๐Ÿ›’ STEP 2: Add Product to Cart'); + console.log('-'.repeat(50)); + + // Get a valid variant first + const productsResult = await saleorFetch(` + query { + products(channel: "default-channel", first: 1) { + edges { + node { + variants { id name } + } + } + } + } + `); + + const variantId = productsResult.products.edges[0].node.variants[0].id; + + const addLineResult = await saleorFetch(` + mutation CheckoutLinesAdd($checkoutId: ID!, $lines: [CheckoutLineInput!]!) { + checkoutLinesAdd(checkoutId: $checkoutId, lines: $lines) { + checkout { + id + token + totalPrice { gross { amount currency } } + subtotalPrice { gross { amount } } + } + errors { field message } + } + } + `, { + checkoutId: checkoutId, + lines: [{ variantId: variantId, quantity: 1 }] + }); + + const afterAdd = addLineResult.checkoutLinesAdd.checkout; + console.log('โœ… Product added'); + console.log(' Product Total:', afterAdd.totalPrice.gross.amount, 'RSD'); + console.log(' Subtotal:', afterAdd.subtotalPrice.gross.amount, 'RSD'); + + // STEP 3: Refresh checkout by token (what refreshCheckout() does) + console.log('\n๐Ÿ”„ STEP 3: Refresh Checkout by Token'); + console.log('-'.repeat(50)); + console.log(' (This simulates what refreshCheckout() does in the store)'); + + const refreshResult = await saleorFetch(` + query GetCheckout($token: UUID!) { + checkout(token: $token) { + id + token + totalPrice { gross { amount currency } } + subtotalPrice { gross { amount } } + } + } + `, { token: checkoutToken }); + + console.log('โœ… Refreshed checkout'); + console.log(' Total from refresh:', refreshResult.checkout.totalPrice.gross.amount, 'RSD'); + + // STEP 4: Set shipping address + console.log('\n๐Ÿ“ STEP 4: Set Shipping Address'); + console.log('-'.repeat(50)); + + const addressResult = await saleorFetch(` + mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: AddressInput!) { + checkoutShippingAddressUpdate(checkoutId: $checkoutId, shippingAddress: $shippingAddress) { + checkout { + id + shippingMethods { id name price { amount currency } } + } + errors { field message } + } + } + `, { + checkoutId: checkoutId, + shippingAddress: { + firstName: "Test", + lastName: "User", + streetAddress1: "123 Test Street", + city: "Belgrade", + postalCode: "11000", + country: "RS", + phone: "+38160123456" + } + }); + + const methods = addressResult.checkoutShippingAddressUpdate.checkout.shippingMethods; + console.log('โœ… Address set'); + console.log(' Available shipping methods:', methods.length); + + if (methods.length === 0) { + console.log('โŒ No shipping methods available!'); + return; + } + + methods.forEach((m, i) => { + console.log(` [${i+1}] ${m.name}: ${m.price.amount} ${m.price.currency}`); + }); + + shippingMethodId = methods[0].id; + const shippingPrice = methods[0].price.amount; + + // STEP 5: Select shipping method (what happens when user clicks radio button) + console.log('\n๐Ÿšš STEP 5: Select Shipping Method'); + console.log('-'.repeat(50)); + console.log(` Selecting: ${methods[0].name} (${shippingPrice} RSD)`); + + const methodResult = await saleorFetch(` + mutation CheckoutShippingMethodUpdate($checkoutId: ID!, $shippingMethodId: ID!) { + checkoutShippingMethodUpdate(checkoutId: $checkoutId, shippingMethodId: $shippingMethodId) { + checkout { + id + totalPrice { gross { amount currency } } + subtotalPrice { gross { amount } } + shippingPrice { gross { amount } } + } + errors { field message } + } + } + `, { + checkoutId: checkoutId, + shippingMethodId: shippingMethodId + }); + + const afterMethod = methodResult.checkoutShippingMethodUpdate.checkout; + console.log('โœ… Shipping method set'); + console.log(' Total:', afterMethod.totalPrice.gross.amount, 'RSD'); + console.log(' Subtotal:', afterMethod.subtotalPrice.gross.amount, 'RSD'); + console.log(' Shipping:', afterMethod.shippingPrice.gross.amount, 'RSD'); + + // STEP 6: Refresh checkout again (what refreshCheckout() does after setting method) + console.log('\n๐Ÿ”„ STEP 6: Refresh Checkout Again'); + console.log('-'.repeat(50)); + console.log(' (Simulating refreshCheckout() call in handleShippingMethodSelect)'); + + const finalRefresh = await saleorFetch(` + query GetCheckout($token: UUID!) { + checkout(token: $token) { + id + token + totalPrice { gross { amount currency } } + subtotalPrice { gross { amount } } + shippingPrice { gross { amount } } + } + } + `, { token: checkoutToken }); + + const final = finalRefresh.checkout; + console.log('โœ… Final checkout state after refresh:'); + console.log(' Total:', final.totalPrice.gross.amount, 'RSD'); + console.log(' Subtotal:', final.subtotalPrice.gross.amount, 'RSD'); + console.log(' Shipping:', final.shippingPrice.gross.amount, 'RSD'); + + // VERIFICATION + console.log('\n๐Ÿ“Š VERIFICATION'); + console.log('=' .repeat(50)); + const expectedTotal = final.subtotalPrice.gross.amount + final.shippingPrice.gross.amount; + const actualTotal = final.totalPrice.gross.amount; + + if (actualTotal === expectedTotal) { + console.log('โœ… PASS: API returns correct total with shipping'); + console.log(` ${final.subtotalPrice.gross.amount} + ${final.shippingPrice.gross.amount} = ${actualTotal}`); + } else { + console.log('โŒ FAIL: API total does not include shipping'); + console.log(` Expected: ${expectedTotal}, Got: ${actualTotal}`); + } + + console.log('\n๐Ÿ” FRONTEND ISSUE ANALYSIS'); + console.log('=' .repeat(50)); + console.log('The API works correctly. The bug is in the frontend.'); + console.log(''); + console.log('What should happen:'); + console.log(' 1. User selects shipping method โ†’ handleShippingMethodSelect()'); + console.log(' 2. Calls checkoutService.updateShippingMethod() โ†’ API updates'); + console.log(' 3. Calls refreshCheckout() โ†’ store updates with new checkout'); + console.log(' 4. Component re-renders with new checkout.totalPrice'); + console.log(''); + console.log('Check browser console for:'); + console.log(' - [Checkout Debug] logs showing totalPrice values'); + console.log(' - Network tab showing the GraphQL mutation/refresh calls'); + console.log(' - React DevTools showing if checkout object updates'); + + } catch (error) { + console.error('\nโŒ Test failed:', error.message); + process.exit(1); + } +} + +runTest(); diff --git a/scripts/test-order-creation.js b/scripts/test-order-creation.js new file mode 100644 index 0000000..3f5e6a8 --- /dev/null +++ b/scripts/test-order-creation.js @@ -0,0 +1,232 @@ +#!/usr/bin/env node +/** + * Full order creation test via API + * Tests complete checkout flow including order completion + */ + +const SALEOR_API_URL = 'https://api.manoonoils.com/graphql/'; + +async function saleorFetch(query, variables = {}) { + const response = await fetch(SALEOR_API_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ query: query.replace(/\n\s*/g, ' '), variables }), + }); + const result = await response.json(); + if (result.errors) { + console.error('GraphQL Error:', JSON.stringify(result.errors, null, 2)); + throw new Error(result.errors[0].message); + } + return result.data; +} + +async function runOrderTest() { + console.log('๐Ÿงช FULL ORDER CREATION TEST ON DEV BRANCH\n'); + console.log('=' .repeat(60)); + + try { + // STEP 1: Create checkout + console.log('\n๐Ÿ“ฆ STEP 1: Create Checkout'); + const createResult = await saleorFetch(` + mutation CheckoutCreate($input: CheckoutCreateInput!) { + checkoutCreate(input: $input) { + checkout { + id + token + totalPrice { gross { amount currency } } + } + errors { field message } + } + } + `, { + input: { + channel: "default-channel", + email: "test-order@example.com", + lines: [], + languageCode: "SR" + } + }); + + const checkoutId = createResult.checkoutCreate.checkout.id; + console.log('โœ… Checkout created:', checkoutId); + + // STEP 2: Get product and add to cart + console.log('\n๐Ÿ›’ STEP 2: Add Product'); + const productsResult = await saleorFetch(` + query { + products(channel: "default-channel", first: 1) { + edges { node { variants { id name } } } + } + } + `); + const variantId = productsResult.products.edges[0].node.variants[0].id; + + await saleorFetch(` + mutation CheckoutLinesAdd($checkoutId: ID!, $lines: [CheckoutLineInput!]!) { + checkoutLinesAdd(checkoutId: $checkoutId, lines: $lines) { + checkout { id } + errors { field message } + } + } + `, { + checkoutId: checkoutId, + lines: [{ variantId: variantId, quantity: 1 }] + }); + console.log('โœ… Product added'); + + // STEP 3: Update email + console.log('\n๐Ÿ“ง STEP 3: Update Email'); + await saleorFetch(` + mutation CheckoutEmailUpdate($checkoutId: ID!, $email: String!) { + checkoutEmailUpdate(checkoutId: $checkoutId, email: $email) { + checkout { id } + errors { field message } + } + } + `, { checkoutId: checkoutId, email: "test-order@example.com" }); + console.log('โœ… Email updated'); + + // STEP 4: Set shipping address + console.log('\n๐Ÿ“ STEP 4: Set Shipping Address'); + await saleorFetch(` + mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: AddressInput!) { + checkoutShippingAddressUpdate(checkoutId: $checkoutId, shippingAddress: $shippingAddress) { + checkout { + id + shippingMethods { id name price { amount } } + } + errors { field message } + } + } + `, { + checkoutId: checkoutId, + shippingAddress: { + firstName: "Test", + lastName: "User", + streetAddress1: "123 Test Street", + city: "Belgrade", + postalCode: "11000", + country: "RS", + phone: "+38160123456" + } + }); + + // Get shipping methods + const methodsResult = await saleorFetch(` + query GetCheckout($token: UUID!) { + checkout(token: $token) { + shippingMethods { id name price { amount } } + } + } + `, { token: createResult.checkoutCreate.checkout.token }); + + const shippingMethodId = methodsResult.checkout.shippingMethods[0].id; + console.log('โœ… Address set, shipping method available:', methodsResult.checkout.shippingMethods[0].name); + + // STEP 5: Set billing address + console.log('\n๐Ÿ’ณ STEP 5: Set Billing Address'); + await saleorFetch(` + mutation CheckoutBillingAddressUpdate($checkoutId: ID!, $billingAddress: AddressInput!) { + checkoutBillingAddressUpdate(checkoutId: $checkoutId, billingAddress: $billingAddress) { + checkout { id } + errors { field message } + } + } + `, { + checkoutId: checkoutId, + billingAddress: { + firstName: "Test", + lastName: "User", + streetAddress1: "123 Test Street", + city: "Belgrade", + postalCode: "11000", + country: "RS", + phone: "+38160123456" + } + }); + console.log('โœ… Billing address set'); + + // STEP 6: Select shipping method + console.log('\n๐Ÿšš STEP 6: Select Shipping Method'); + await saleorFetch(` + mutation CheckoutShippingMethodUpdate($checkoutId: ID!, $shippingMethodId: ID!) { + checkoutShippingMethodUpdate(checkoutId: $checkoutId, shippingMethodId: $shippingMethodId) { + checkout { + id + totalPrice { gross { amount } } + subtotalPrice { gross { amount } } + shippingPrice { gross { amount } } + } + errors { field message } + } + } + `, { checkoutId: checkoutId, shippingMethodId: shippingMethodId }); + console.log('โœ… Shipping method selected'); + + // STEP 7: Complete checkout (create order) + console.log('\nโœ… STEP 7: Complete Checkout (Create Order)'); + console.log('-'.repeat(60)); + + const completeResult = await saleorFetch(` + mutation CheckoutComplete($checkoutId: ID!) { + checkoutComplete(checkoutId: $checkoutId) { + order { + id + number + status + created + total { + gross { amount currency } + } + subtotal { + gross { amount } + } + shippingPrice { + gross { amount } + } + } + errors { field message } + } + } + `, { checkoutId: checkoutId }); + + if (completeResult.checkoutComplete.errors?.length > 0) { + throw new Error(`Order creation failed: ${completeResult.checkoutComplete.errors[0].message}`); + } + + const order = completeResult.checkoutComplete.order; + + console.log('โœ… ORDER CREATED SUCCESSFULLY!'); + console.log(''); + console.log('Order Details:'); + console.log(' Order ID:', order.id); + console.log(' Order Number:', order.number); + console.log(' Status:', order.status); + console.log(' Created:', order.created); + console.log(''); + console.log('Pricing:'); + console.log(' Subtotal:', order.subtotal.gross.amount, 'RSD'); + console.log(' Shipping:', order.shippingPrice.gross.amount, 'RSD'); + console.log(' Total:', order.total.gross.amount, 'RSD'); + + // Verification + const expectedTotal = order.subtotal.gross.amount + order.shippingPrice.gross.amount; + console.log(''); + console.log('๐Ÿ“Š VERIFICATION:'); + if (order.total.gross.amount === expectedTotal) { + console.log('โœ… PASS: Order total includes shipping correctly'); + console.log(` ${order.subtotal.gross.amount} + ${order.shippingPrice.gross.amount} = ${order.total.gross.amount}`); + } else { + console.log('โŒ FAIL: Order total does not match expected'); + } + + console.log(''); + console.log('๐ŸŽ‰ DEV BRANCH TEST COMPLETE - ALL SYSTEMS GO!'); + + } catch (error) { + console.error('\nโŒ Test failed:', error.message); + process.exit(1); + } +} + +runOrderTest(); diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index b7c9568..14932f7 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -3,6 +3,8 @@ import { NextIntlClientProvider } from "next-intl"; import { getMessages, setRequestLocale } from "next-intl/server"; import { SUPPORTED_LOCALES, DEFAULT_LOCALE, isValidLocale } from "@/lib/i18n/locales"; import { OpenPanelComponent } from "@openpanel/nextjs"; +import Script from "next/script"; +import { RYBBIT_HOST, RYBBIT_SITE_ID } from "@/lib/services/RybbitService"; const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://dev.manoonoils.com"; @@ -46,13 +48,18 @@ export default async function LocaleLayout({ return ( <> - + +