I'm creating a ecommerce website and I'm having some issues with my cloudinary uploads. When I use the admin panel to upload there are on issues, but when I try to upload from my website it works somewhat, but my image fetching appends the correct link onto another cloudinary link
For example: When I upload an image via the website and not the admin panel, the alt attribute renders into my website, but when i look into the console its trying to fetch this url:
Which is the correct image url appended to "https://res.cloudinary.com/dt4sw7qtl/image/upload/v1/media/"
Links split: https://res.cloudinary.com/dt4sw7qtl/image/upload/v1/media/
https://res.cloudinary.com/dt4sw7qtl/image/upload/v1711814695/wmtt2ry1yycxdukggs3f.jpg
If i upload a working image from the admin panel and move on with testing the order, the image is shown working all the way until the order payment screen, where it also tries to fetch an image appended to the previous link in the same way.
Let me know if I need to include some more code to make sense out of this.
At the bottom I have a screen similar to the order page, the order summary, which is working as it should, if somone would want to compare to find the issue.
Here is the upload image screen code:
import React, { useState, useEffect } from 'react'
import axios from 'axios'
import { Link } from 'react-router-dom'
import { Form, Button } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import Loader from '../components/Loader'
import Message from '../components/Message'
import FormContainer from '../components/FormContainer'
import { listProductDetails, updateProduct } from '../actions/productActions'
import { PRODUCT_UPDATE_RESET } from '../constants/productConstants'
function ProductEditScreen({ match, history }) {
const productId = match.params.id
const [name, setName] = useState('')
const [price, setPrice] = useState('0')
const [image, setImage] = useState('')
const [brand, setBrand] = useState('')
const [category, setCategory] = useState('')
const [countInStock, setCountInStock] = useState('0')
const [description, setDescription] = useState('')
const [uploading, setUploading] = useState(false)
const dispatch = useDispatch()
const productDetails = useSelector(state => state.productDetails)
const { error, loading, product } = productDetails
const productUpdate = useSelector(state => state.productUpdate)
const { error: errorUpdate, loading: loadingUpdate, success: successUpdate } = productUpdate
useEffect(() => {
if (successUpdate) {
dispatch({ type: PRODUCT_UPDATE_RESET })
history.push('/admin/productlist')
} else {
if (!product.name || product._id !== Number(productId)) {
dispatch(listProductDetails(productId))
} else {
setName(product.name)
setPrice(product.price)
setImage(product.image)
setBrand(product.brand)
setCategory(product.category)
setCountInStock(product.countInStock)
setDescription(product.description)
}
}
}, [dispatch, product, productId, history, successUpdate])
const submitHandler = (e) => {
e.preventDefault()
dispatch(updateProduct({
_id: productId,
name,
price,
image,
brand,
category,
countInStock,
description
}))
}
const uploadFileHandler = async (e) => {
const file = e.target.files[0]
const formData = new FormData()
formData.append('product_id', productId)
formData.append('image', file)
setUploading(true)
try {
const config = {
headers: {
'Content-Type': 'multipart/form-data'
}
}
const { data } = await axios.post('MyUrlRemovedForQuestion', formData, config)
setImage(data.image_url)
setUploading(false)
} catch (error) {
setUploading(false)
}
}
return (
<div>
<Link to='/admin/productlist'>
Go Back
</Link>
<FormContainer>
<h1>Edit Product</h1>
{loadingUpdate && <Loader />}
{errorUpdate && <Message variant='danger'>{errorUpdate}</Message>}
{loading ? <Loader /> : error ? <Message variant='danger'>{error}</Message>
: (
<Form onSubmit={submitHandler}>
<Form.Group controlId='name'>
<Form.Label>Name</Form.Label>
<Form.Control
type='name'
placeholder='Enter name'
value={name}
onChange={(e) => setName(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='price'>
<Form.Label>Price</Form.Label>
<Form.Control
type='number'
placeholder='Enter price'
value={price}
onChange={(e) => setPrice(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='image'>
<Form.Label>Image</Form.Label>
<Form.Control
type='image'
placeholder='Enter image'
value={image}
onChange={(e) => setImage(e.target.value)}
>
</Form.Control>
<Form.Control
type='file'
custom
onChange={uploadFileHandler}
>
</Form.Control>
{uploading && <Loader />}
</Form.Group>
<Form.Group controlId='brand'>
<Form.Label>Brand</Form.Label>
<Form.Control
type='text'
placeholder='Enter brand'
value={brand}
onChange={(e) => setBrand(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='countInStock'>
<Form.Label>Stock</Form.Label>
<Form.Control
type='number'
placeholder='Enter stock'
value={countInStock}
onChange={(e) => setCountInStock(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='category'>
<Form.Label>Category</Form.Label>
<Form.Control
type='text'
placeholder='Enter category'
value={category}
onChange={(e) => setCategory(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='description'>
<Form.Label>Description</Form.Label>
<Form.Control
type='text'
placeholder='Enter description'
value={description}
onChange={(e) => setDescription(e.target.value)}
>
</Form.Control>
</Form.Group>
<Button type='submit' variant='primary'>
Update
</Button>
</Form>
)}
</FormContainer >
</div>
)
}
export default ProductEditScreen
Here is the order summary screen code:
import React, { useState, useEffect } from 'react'
import { Button, Row, Col, ListGroup, Image, Card, ListGroupItem } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import { PayPalButton } from 'react-paypal-button-v2'
import { Link } from 'react-router-dom'
import Message from '../components/Message'
import Loader from '../components/Loader'
import { getOrderDetails, payOrder } from '../actions/orderActions'
import { ORDER_PAY_RESET } from '../constants/orderConstants'
function OrderScreen({ match }) {
const orderId = match.params.id
const dispatch = useDispatch()
const [SdkReady, setSdkReady] = useState(false)
const orderDetails = useSelector(state => state.orderDetails)
const { order, error, loading } = orderDetails
const orderPay = useSelector(state => state.orderPay)
const { loading: loadingPay, success: successPay } = orderPay
if (!loading && !error) {
order.itemsPrice = order.orderItems.reduce((acc, item) => acc + item.price * item.qty, 0).toFixed(2)
}
const addPayPalScript = () => {
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = ''
script.async = true
script.onload = () => {
setSdkReady(true)
}
document.body.appendChild(script)
}
useEffect(() => {
if (!order || successPay || order._id !== Number(orderId)) {
dispatch({ type: ORDER_PAY_RESET })
dispatch(getOrderDetails(orderId))
} else if (!order.isPaid) {
if (!window.paypal) {
addPayPalScript()
} else {
setSdkReady(true)
}
}
}, [dispatch, order, orderId, successPay])
const successPaymentHandler = (paymentResult) => {
dispatch(payOrder(orderId, paymentResult))
}
return loading ? (
<Loader />
) : error ? (
<Message variant='danger'>{error}</Message>
) : (
<div>
<h1>Order: {order._id}</h1>
<Row>
<Col md={8}>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Shipping</h2>
<p><strong>Name: </strong> {order.user.name}</p>
<p><strong>Email: </strong> <a href={`mailto:${order.user.email}`}>{order.user.email}</a></p>
<p>
<strong>To: </strong>
{order.shippingAddress.address}, {order.shippingAddress.city}
{' '}
{order.shippingAddress.postalCode},
{' '}
{order.shippingAddress.country}
</p>
{order.isDelivered ? (
<Message variant='success'>Delivered on {order.deliveredAt}</Message>
) : (
<Message variant='warning'>Not delivered{order.deliveredAt}</Message>
)}
</ListGroup.Item>
<ListGroup.Item>
<h2>Payment Method</h2>
<p>
<strong>Method:</strong>
{order.paymentMethod}
</p>
{order.isPaid ? (
<Message variant='success'>Paid on {order.paidAt}</Message>
) : (
<Message variant='warning'>Not paid{order.paidAt}</Message>
)}
</ListGroup.Item>
<ListGroup.Item>
<h2>Order Items</h2>
{order.orderItems.length === 0 ? <Message variant='info'>
Order is empty
</Message> : (
<ListGroup variant='flush'>
{order.orderItems.map((item, index, product) => (
<ListGroup.Item key={index}>
<Row>
<Col md={1}>
<Image src={item.image} alt={item.name} fluid rounded />
</Col>
<Col>
<Link to={`/product/${item.product}`}>{item.name}</Link>
</Col>
<Col md={4}>
{item.qty} X ${item.price} = ${(item.qty * item.price).toFixed(2)}
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</ListGroup.Item>
</ListGroup>
</Col>
<Col md={4}>
<Card>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Order Summary</h2>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Items:</Col>
<Col>${order.itemsPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Shipping:</Col>
<Col>${order.shippingPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Tax:</Col>
<Col>${order.taxPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Total:</Col>
<Col>${order.totalPrice}</Col>
</Row>
</ListGroup.Item>
{!order.isPaid && (
<ListGroup.Item>
{loadingPay && <Loader />}
{!SdkReady ? (
<Loader />
) : (
<PayPalButton
amount={order.totalPrice}
onSuccess={successPaymentHandler}
/>
)}
</ListGroup.Item>
)}
</ListGroup>
</Card>
</Col>
</Row>
</div>
)
}
export default OrderScreen
WORKING screen similar to orderscreen
import React, { useState, useEffect } from 'react'
import { Button, Row, Col, ListGroup, Image, Card, ListGroupItem } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import CheckoutSteps from '../components/CheckoutSteps'
import Message from '../components/Message'
import { createOrder } from '../actions/orderActions'
import { ORDER_CREATE_RESET } from '../constants/orderConstants'
function PlaceOrderScreen({ history }) {
const orderCreate = useSelector(state => state.orderCreate)
const { order, error, success } = orderCreate
const dispatch = useDispatch()
const cart = useSelector(state => state.cart)
cart.itemsPrice = cart.cartItems.reduce((acc, item) => acc + item.price * item.qty, 0).toFixed(2)
cart.shippingPrice = (cart.itemsPrice > 100 ? 0 : 10).toFixed(2)
cart.taxPrice = Number((0.082) * cart.itemsPrice).toFixed(2)
cart.totalPrice = (Number(cart.itemsPrice) + Number(cart.shippingPrice) + Number(cart.taxPrice)).toFixed(2)
if (!cart.paymentMethod) {
history.push('/payment')
}
useEffect(() => {
if (success) {
history.push(`/order/${order._id}`)
dispatch({ type: ORDER_CREATE_RESET })
}
}, [success, history])
const placeOrder = () => {
dispatch(createOrder({
orderItems: cart.cartItems,
shippingAddress: cart.shippingAddress,
paymentMethod: cart.paymentMethod,
itemsPrice: cart.itemsPrice,
shippingPrice: cart.shippingPrice,
taxPrice: cart.taxPrice,
totalPrice: cart.totalPrice,
}))
}
return (
<div>
<CheckoutSteps step1 step2 step3 step4 />
<Row>
<Col md={8}>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Shipping</h2>
<p>
<strong>To: </strong>
{cart.shippingAddress.address}, {cart.shippingAddress.city}
{' '}
{cart.shippingAddress.postalCode},
{' '}
{cart.shippingAddress.country}
</p>
</ListGroup.Item>
<ListGroup.Item>
<h2>Payment Method</h2>
<p>
<strong>Method:</strong>
{cart.paymentMethod}
</p>
</ListGroup.Item>
<ListGroup.Item>
<h2>Order Items</h2>
{cart.cartItems.length === 0 ? <Message variant='info'>
Your cart is empty
</Message> : (
<ListGroup variant='flush'>
{cart.cartItems.map((item, index) => (
<ListGroup.Item key={index}>
<Row>
<Col md={1}>
<Image src={item.image} alt={item.name} fluid rounded />
</Col>
<Col>
<Link to={`/product/${item.product}`}>{item.name}</Link>
</Col>
<Col md={4}>
{item.qty} X ${item.price} = ${(item.qty * item.price).toFixed(2)}
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</ListGroup.Item>
</ListGroup>
</Col>
<Col md={4}>
<Card>
<ListGroup variant='flush'>
<ListGroup.Item>
<h2>Order Summary</h2>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Items:</Col>
<Col>${cart.itemsPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Shipping:</Col>
<Col>${cart.shippingPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Tax:</Col>
<Col>${cart.taxPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Total:</Col>
<Col>${cart.totalPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
{error && <Message variant='danger'>{error}</Message>}
</ListGroup.Item>
<ListGroup.Item>
<div className='d-grid gap-2'>
<Button
type='button'
disabled={cart.cartItems === 0}
onClick={placeOrder}
>Place Order</Button>
</div>
</ListGroup.Item>
</ListGroup>
</Card>
</Col>
</Row>
</div>
)
}
export default PlaceOrderScreen
Let me know if this question was formed incorrectly or if i need to add anything. Thank you in advance!
Tried to load images from cloudinary but they were appended to another cloudinary link