Skip links

Building a Shopify Delivery Routing GPS App Using React & Remix

Shopify React Remix App development can be a great way to launch your customized Ecommerce solution. Shopify allows development using any framework you want, but they have most of their developer pipeline set to use React + Remix + Shopify Polaris UI Components.

App Blueprint: What Are The Requirements?

Our goal is to build an APP that allows store administrators to visualize all of their orders on a map. From there an administrator can add delivery drivers. The app will then allow manually selecting orders and assigning them to delivery drivers. After the delivery list is created, the delivery list can be texted via WhatsApp or SMS to the delivery driver. 

For a phase 2 we can add-on the ability of delivery drivers to have a login to the App. From that login they will see the list of deliveries they have scheduled for that day. As they make deliveries they can mark them off as delivered, accept customer signatures for acceptance of delivery, and route their day’s deliveries on a map. 

Which Map Service To use?

The next consideration is which mapping service to use. The 2 main contenders are Mapbox and Google Maps. 

I reviewed Mapbox documentation and they only had one page about implementing their mapping system using React. Since all of the App will depend upon complex map interactions, I instead decided to go with Google Maps. The Vis.gl React Google Maps documentation is very thorough and explains how to do things such as use a maps library, render routes, and customize markers and infowindows.

After developing the GPS routing app though, Mapbox has since added more documentation about developing React Apps with Mapbox using Search JS. 

Adding The Main Map

The routes/api.index.jsx folder will have some code like this:

				
					              <InlineStack>
                <div style={{width:"55%", paddingRight: "20px"}}>
                  <Card>
                    <BlockStack gap="200">
                      <APIProvider apiKey={settings.googleApiKey} libraries={['marker']}>
                        <MapWrapper/>
                      </APIProvider>
                    </BlockStack>

                  </Card>
                </div>
                <div style={{width:"45%"}}>
                  <Card>
                    <div style={{marginBottom:"10px"}}><Text variant="headingMd">
                      Drivers
                    </Text></div>
                    {drivers.map((driver, index) => {
                      const route = routes.find(route => route.driverId === driver.id);
                      return (
                        <Driver  driver={driver} key={index}
                                 index={index} route={route}
                        />
                      )
                    })}
                  </Card>
                </div>
              </InlineStack>
				
			

Here we are passing in the google api key using settings from the database table and it is passed to the MapWrapper component. 

The next section of code will map over our drivers. The drivers will show on the right hand column of the page. Drivers can also be assigned routes.

Here is the code for the app/components/mapwrapper.jsx.

				
					return (
    <Map
      id="mainmap"
      mapId={'mainmapid'}
      style={{width: '100%', height: '500px'}}
      defaultCenter={{lat: 37.7749, lng: -122.4194}}
      defaultZoom={7}
    >
      {!loading && memoizedMarkers.map(marker => (
        <MarkerWithInfoWindow
          markerObj={marker}
          key={marker.key}
          position={marker.position}
          orderData={marker.orderData}
        />
      ))}
      {driversPositions.map((driver, index) => (
        <TruckMarker driver={driver} key={index} />
      ))}
      {drivers.map((driver, index) => {
        return <Directionrender key={index} driver={driver} />
      })}

    </Map>
  );
				
			

This section of code is loading the Google Map React component according to the documentation of Vis.gl. 

After that we map over the drivers positions. These driver positions are updated via the React Native app on their mobile devices every 30 seconds and stored in the database. As they drive, their position on the map will be updated. 

The next section of code is for rendering directions which actually means the Routes on the map. The routes show on the map in different colors based on the driver. 

Updating Drivers via REST API Using Prisma ORM

				
					export async function action({ request }) {

  const { admin } = await authenticate.admin(request);

  const method = request.method;

  let data, name, phone, email, carcolor, profilepicture, password, shop, address, response, profilepictureid;
  data = await request.formData();
  // console.log("data", data);
  data = Object.fromEntries(data);
  name = data.name;
  phone = data.phone;
  email = data.email;
  carcolor = data.carcolor;
  profilepicture = data.profilepicture;
  password = data.password;
  shop = data.shop;
  address = data.address;
  profilepictureid = data.profilepictureid;

  switch (method) {
    case "POST":
      // Handle POST request logic here
      const driver = await db.drivers.create({
        data: {
          name,
          phone,
          email,
          carcolor,
          profilepicture,
          profilepictureid,
          password,
          shop,
          address
        },
      });

      response = json({message: "Driver added", method: "POST", driver: driver});
      return cors(request, response);

    case "PATCH":

      await db.drivers.update({
        where: {
          id: Number(data.id),
          shop: shop
        },
        data: {
          name,
          phone,
          email,
          carcolor,
          profilepicture,
          profilepictureid,
          profilepicturefinal: null,
          password,
          shop,
          address
        }
      });
      return json({message: "Success", method: "PATCH"});
      break;


    case "DELETE":
      const resources = JSON.parse(data.ids)
      console.log('data in delete', data)
      console.log('resources in delete', resources)
      // Handle DELETE request logic here
      resources.forEach(async (id) => {
        await db.drivers.delete({
          where: {
            id: Number(id),
            shop: shop
          },
        });
      });

      response = json({ message: "Drivers deleted", method: "DELETE", driver: false });
      // For example, removing an item from the wishlist
      return cors(request, response);
      break;

    default:
      // Optional: handle other methods or return a method not allowed response
      return new Response("Method Not Allowed", { status: 405 });
  }
				
			

All of the driver data is able to be saved to the database using the Prisma ORM. 

GraphQL Uploading of Images

One of the most difficult parts of the driver adding and updating is getting the profile picture to work. This requires uploading images via GraphQL so that they are properly stored in the Shopify content file area. Once images are stored using this method, Shopify has to process the file and then after a few seconds will return a preview image url. I will not add all the GraphQL code here but I will put it in github so you can refer to it if needed. But the part that queries for the preview image url is like this:

 

				
					const toUpdateProfilePictures = await db.drivers.findMany({
    where: {
      profilepicturefinal: {
        equals: null
      },
      profilepictureid: {
        not: null
      }
    }
  });

  console.log('toUpdateProfilePictures', toUpdateProfilePictures)

  let responseData = [];

  toUpdateProfilePictures.forEach(async (driver) => {

    const response = await admin.graphql(`query {
      node(id: "${driver.profilepictureid}"){
        ... on MediaImage {
          id
          preview {
            image {
              url
              originalSrc
              transformedSrc
            }
          }
        }
      }
    }`);

    const data = await response.json();

    console.log('graphql data', data)

    const profilepicture = data.data.node.preview.image.url;

    const dbupdate = await db.drivers.update({
      where: {
        id: driver.id
      },
      data: {
        profilepicturefinal: profilepicture
      }
    });

    responseData.push(dbupdate);

  });
				
			

Showing Routes and Sending Routes Via WhatsApp

Using the send via WhatsApp button successfully sends the message

Custom Ecommerce Web Developer for Shopify React Remix Apps

I’ll keep this blog updated with my latest Shopify App development projects and code samples. For getting your custom Shopify App developed with great architecture and speed there is no better choice than Kevin Javitz Ecommerce Web Development. I am fluent with a variety of App development patterns including React Native for iOS and Android Apps that tie-in to your Shopify store. Please let me know what you are looking for and I’ll get back to you with a free proposal.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.

Building a Shopify Delivery Routing GPS App Using React & Remix

Shopify React Remix App development can be a great way to launch your customized Ecommerce solution. Shopify allows development using any framework you want, but they have most of their developer pipeline set to use React + Remix + Shopify Polaris UI Components.

App Blueprint: What Are The Requirements?

Our goal is to build an APP that allows store administrators to visualize all of their orders on a map. From there an administrator can add delivery drivers. The app will then allow manually selecting orders and assigning them to delivery drivers. After the delivery list is created, the delivery list can be texted via WhatsApp or SMS to the delivery driver. 

For a phase 2 we can add-on the ability of delivery drivers to have a login to the App. From that login they will see the list of deliveries they have scheduled for that day. As they make deliveries they can mark them off as delivered, accept customer signatures for acceptance of delivery, and route their day’s deliveries on a map. 

Which Map Service To use?

The next consideration is which mapping service to use. The 2 main contenders are Mapbox and Google Maps. 

I reviewed Mapbox documentation and they only had one page about implementing their mapping system using React. Since all of the App will depend upon complex map interactions, I instead decided to go with Google Maps. The Vis.gl React Google Maps documentation is very thorough and explains how to do things such as use a maps library, render routes, and customize markers and infowindows.

After developing the GPS routing app though, Mapbox has since added more documentation about developing React Apps with Mapbox using Search JS. 

Adding The Main Map

The routes/api.index.jsx folder will have some code like this:

				
					              <InlineStack>
                <div style={{width:"55%", paddingRight: "20px"}}>
                  <Card>
                    <BlockStack gap="200">
                      <APIProvider apiKey={settings.googleApiKey} libraries={['marker']}>
                        <MapWrapper/>
                      </APIProvider>
                    </BlockStack>

                  </Card>
                </div>
                <div style={{width:"45%"}}>
                  <Card>
                    <div style={{marginBottom:"10px"}}><Text variant="headingMd">
                      Drivers
                    </Text></div>
                    {drivers.map((driver, index) => {
                      const route = routes.find(route => route.driverId === driver.id);
                      return (
                        <Driver  driver={driver} key={index}
                                 index={index} route={route}
                        />
                      )
                    })}
                  </Card>
                </div>
              </InlineStack>
				
			

Here we are passing in the google api key using settings from the database table and it is passed to the MapWrapper component. 

The next section of code will map over our drivers. The drivers will show on the right hand column of the page. Drivers can also be assigned routes.

Here is the code for the app/components/mapwrapper.jsx.

				
					return (
    <Map
      id="mainmap"
      mapId={'mainmapid'}
      style={{width: '100%', height: '500px'}}
      defaultCenter={{lat: 37.7749, lng: -122.4194}}
      defaultZoom={7}
    >
      {!loading && memoizedMarkers.map(marker => (
        <MarkerWithInfoWindow
          markerObj={marker}
          key={marker.key}
          position={marker.position}
          orderData={marker.orderData}
        />
      ))}
      {driversPositions.map((driver, index) => (
        <TruckMarker driver={driver} key={index} />
      ))}
      {drivers.map((driver, index) => {
        return <Directionrender key={index} driver={driver} />
      })}

    </Map>
  );
				
			

This section of code is loading the Google Map React component according to the documentation of Vis.gl. 

After that we map over the drivers positions. These driver positions are updated via the React Native app on their mobile devices every 30 seconds and stored in the database. As they drive, their position on the map will be updated. 

The next section of code is for rendering directions which actually means the Routes on the map. The routes show on the map in different colors based on the driver. 

Updating Drivers via REST API Using Prisma ORM

				
					export async function action({ request }) {

  const { admin } = await authenticate.admin(request);

  const method = request.method;

  let data, name, phone, email, carcolor, profilepicture, password, shop, address, response, profilepictureid;
  data = await request.formData();
  // console.log("data", data);
  data = Object.fromEntries(data);
  name = data.name;
  phone = data.phone;
  email = data.email;
  carcolor = data.carcolor;
  profilepicture = data.profilepicture;
  password = data.password;
  shop = data.shop;
  address = data.address;
  profilepictureid = data.profilepictureid;

  switch (method) {
    case "POST":
      // Handle POST request logic here
      const driver = await db.drivers.create({
        data: {
          name,
          phone,
          email,
          carcolor,
          profilepicture,
          profilepictureid,
          password,
          shop,
          address
        },
      });

      response = json({message: "Driver added", method: "POST", driver: driver});
      return cors(request, response);

    case "PATCH":

      await db.drivers.update({
        where: {
          id: Number(data.id),
          shop: shop
        },
        data: {
          name,
          phone,
          email,
          carcolor,
          profilepicture,
          profilepictureid,
          profilepicturefinal: null,
          password,
          shop,
          address
        }
      });
      return json({message: "Success", method: "PATCH"});
      break;


    case "DELETE":
      const resources = JSON.parse(data.ids)
      console.log('data in delete', data)
      console.log('resources in delete', resources)
      // Handle DELETE request logic here
      resources.forEach(async (id) => {
        await db.drivers.delete({
          where: {
            id: Number(id),
            shop: shop
          },
        });
      });

      response = json({ message: "Drivers deleted", method: "DELETE", driver: false });
      // For example, removing an item from the wishlist
      return cors(request, response);
      break;

    default:
      // Optional: handle other methods or return a method not allowed response
      return new Response("Method Not Allowed", { status: 405 });
  }
				
			

All of the driver data is able to be saved to the database using the Prisma ORM. 

GraphQL Uploading of Images

One of the most difficult parts of the driver adding and updating is getting the profile picture to work. This requires uploading images via GraphQL so that they are properly stored in the Shopify content file area. Once images are stored using this method, Shopify has to process the file and then after a few seconds will return a preview image url. I will not add all the GraphQL code here but I will put it in github so you can refer to it if needed. But the part that queries for the preview image url is like this:

 

				
					const toUpdateProfilePictures = await db.drivers.findMany({
    where: {
      profilepicturefinal: {
        equals: null
      },
      profilepictureid: {
        not: null
      }
    }
  });

  console.log('toUpdateProfilePictures', toUpdateProfilePictures)

  let responseData = [];

  toUpdateProfilePictures.forEach(async (driver) => {

    const response = await admin.graphql(`query {
      node(id: "${driver.profilepictureid}"){
        ... on MediaImage {
          id
          preview {
            image {
              url
              originalSrc
              transformedSrc
            }
          }
        }
      }
    }`);

    const data = await response.json();

    console.log('graphql data', data)

    const profilepicture = data.data.node.preview.image.url;

    const dbupdate = await db.drivers.update({
      where: {
        id: driver.id
      },
      data: {
        profilepicturefinal: profilepicture
      }
    });

    responseData.push(dbupdate);

  });
				
			

Showing Routes and Sending Routes Via WhatsApp

Using the send via WhatsApp button successfully sends the message

Custom Ecommerce Web Developer for Shopify React Remix Apps

I’ll keep this blog updated with my latest Shopify App development projects and code samples. For getting your custom Shopify App developed with great architecture and speed there is no better choice than Kevin Javitz Ecommerce Web Development. I am fluent with a variety of App development patterns including React Native for iOS and Android Apps that tie-in to your Shopify store. Please let me know what you are looking for and I’ll get back to you with a free proposal.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.

Explore
Drag