Progress bar for Apollo client upload

Hi, I’m using React frontend and Nodejs Backend.
I want to add a progress bar on my frontend.

Here is my apolloClient setup:

Blockquote

import { ApolloClient, InMemoryCache } from ‘@apollo/client’;
import createUploadLink from ‘apollo-upload-client/createUploadLink.mjs’;

const uploadLink = createUploadLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT, // Use the environment variable
});

const client = new ApolloClient({
link: uploadLink,
cache: new InMemoryCache(),
});

export default client;

Blockquote

and here is my component:

Blockquote

import React, { useState } from ‘react’;
import { useParams } from ‘react-router-dom’;
import { useQuery, useMutation, gql } from ‘@apollo/client’;
import MerchandiseItem from ‘…/components/MerchandiseItem’;

const GET_MERCH_PAGE = gql`
query GetMerchPage($input: MerchInput!) {
getMerchPage(input: $input) {
id
title
description
ownerId
items {
id
title
description
price
attributes
mainImage
merchandisePageId
}
}
}

`;

const CREATE_MERCH_ITEM = gql mutation Mutation($input: MerchandiseItemInput!) { CreateMerchandiseItem(input: $input) { id title description merchandisePageId mainImage } };

const UPDATE_MERCH_ITEM = gql mutation UpdateMerchItem($id: ID!, $input: MerchItemInput!) { updateMerchItem(id: $id, input: $input) { id title description price attributes mainImage additionalImages merchandisePageId } };

const DELETE_MERCH_ITEM = gql mutation DeleteMerchItem($id: ID!) { deleteMerchItem(id: $id) { id } };

// Your existing GraphQL queries and mutations here…

const MerchandiseDashboard = () => {
const { merchPageId } = useParams();
const token = sessionStorage.getItem(‘token’);
const MerchInput = merchPageId;
const [progress, setProgress] = useState(0);

const { loading, error, data, refetch } = useQuery(GET_MERCH_PAGE, {
variables: { input: { id: merchPageId } },
context: {
headers: {
Authorization: Bearer ${token},
},
},
});

const [createMerchItem] = useMutation(CREATE_MERCH_ITEM,{
context: {
fetchOptions: {
onUploadProgress: (event) => {
if (event.lengthComputable) {
const percentCompleted = Math.round((event.loaded * 100) / event.total);
setProgress(percentCompleted); // Update the progress state
}
},
},
},
});

const [updateMerchItem] = useMutation(UPDATE_MERCH_ITEM);
const [deleteMerchItem] = useMutation(DELETE_MERCH_ITEM);

const [editingItem, setEditingItem] = useState(null);
const [newItem, setNewItem] = useState({ title: ‘’, description: ‘’, price: 0, mainImageFile: null });

const [imageFile, setImageFile] = useState(null); // State to store the selected file

if (loading) return

Loading…

;
if (error) return

Error: {error.message}

;

const { title, description, items } = data.getMerchPage;
const backgroundimage = https://gigabout.com.au/merchimages/${merchPageId}/background.jpg;

// Handle image file change
const handleImageChange = async (e) => {
console.log(“we made it into handleImageChange”)
const file = e.target.files[0]; // Get the selected file
if (file) {
const fileSizeInMB = file.size / (1024 * 1024); // Convert size to MB
if (fileSizeInMB > 6) {
alert(“File size exceeds 6 MB. Please choose a smaller file.”);
return;
}
try {

    console.log(file.name)
    setNewItem({ ...newItem, mainImageFile: file });    // Update newItem with base64 image
  } catch (error) {
    console.error('Error:', error);
  }
}

};

const handleCreateOrUpdate = async () => {
if (editingItem) {
await updateMerchItem({ variables: { id: editingItem.id, input: newItem } });
} else {
console.log(newItem)

  await createMerchItem({
    variables: {
      input: {
        title: newItem.title,
        description: newItem.description,
        price: newItem.price,
        mainImageFile: newItem.mainImageFile, // Pass the file directly
        merchandisePageId: merchPageId
      }
    }
  });
}
refetch();
setNewItem({ title: '', description: '', price: 0, mainImage: '', additionalImages: [] });
setEditingItem(null);

};

const handleEdit = (item) => {
setEditingItem(item);
setNewItem(item);
};

const handleDelete = async (id) => {
await deleteMerchItem({ variables: { id } });
refetch();
};

return (
<div
style={{
position: ‘relative’,
minHeight: ‘100vh’,
backgroundImage: url(${backgroundimage}),
backgroundRepeat: ‘repeat’,
backgroundSize: ‘cover’,
}}
>
<div
style={{
position: ‘absolute’,
top: 0,
left: 0,
width: ‘100%’,
height: ‘100%’,
backgroundColor: ‘rgba(255, 255, 255, 0.886)’,
zIndex: 1,
}}
/>
<div style={{ position: ‘relative’, zIndex: 2 }}>

{title}

    <p>{description}</p>

    {/* Merchandise Item Form */}
    <div className="merchandise-form">
      <input
        type="text"
        placeholder="Title"
        value={newItem.title}
        onChange={(e) => setNewItem({ ...newItem, title: e.target.value })}
      />
      <textarea
        placeholder="Description"
        value={newItem.description}
        onChange={(e) => setNewItem({ ...newItem, description: e.target.value })}
      />
      <input
        type="number"
        placeholder="Price"
        value={newItem.price}
        onChange={(e) => setNewItem({ ...newItem, price: parseFloat(e.target.value) })}
      />
      
      {/* Image Picker */}
      <input
        type="file"
        accept="image/png, image/jpeg"
        onChange={handleImageChange}
      />
      
      <button onClick={handleCreateOrUpdate}>{editingItem ? 'Update Item' : 'Create Item'}</button>
      {editingItem && <button onClick={() => setEditingItem(null)}>Cancel Edit</button>}
       {/* Display the progress */}
       {progress > 0 && <div>Upload progress: {progress}%</div>}
    </div>
    {/* Progress Bar */}
    {progress > 0 && (
      <div style={{ margin: '10px 0' }}>
        <div style={{
          width: '100%',
          height: '10px',
          backgroundColor: '#e0e0e0',
          borderRadius: '5px',
        }}>
          <div style={{
            width: `${progress}%`,
            height: '100%',
            backgroundColor: progress < 100 ? '#3b82f6' : '#4caf50', // Blue during upload, green when complete
            borderRadius: '5px',
            transition: 'width 0.3s ease',
          }} />
        </div>
        <p>{progress}%</p> {/* Optional to show the percentage as text */}
      </div>
    )}
    

    {/* Merchandise Items List */}
    <h2>Manage Products</h2>
    <div style={{ paddingLeft: 20, paddingRight: 20 }}>
      <div
        style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
          gap: '16px',
          backgroundColor: 'transparent',
        }}
      >
        {items.map((item) => (
          <div key={item.id}>
            <MerchandiseItem item={item} />
            <button onClick={() => handleEdit(item)}>Edit</button>
            <button onClick={() => handleDelete(item.id)}>Delete</button>
          </div>
        ))}
      </div>
    </div>
  </div>
</div>

);
};

export default MerchandiseDashboard;