### Check Node.js Installation Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Verifies if Node.js is installed and displays its version. Requires Node.js to be installed on the system. ```Bash node -v ``` -------------------------------- ### Set up Express Server Entry Point Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Initializes an Express.js server, sets up middleware for CORS and body parsing, and starts the server listening on port 3000. This serves as the main entry point for the application. ```javascript const express = require('express'); const app = express(); app.use(require('cors')()); app.use(require('body-parser').urlencoded({ extended: true })); app.listen(3000, (err) => { if (err) throw err; console.log(`Listening on http://localhost:3000`); }); ``` -------------------------------- ### Check Docker Installation Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Verifies if Docker is installed and displays its version. Requires Docker to be installed on the system. ```Bash docker -v ``` -------------------------------- ### Install React App Rewired Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Installs `react-app-rewired` as a development dependency. This package allows customization of the `create-react-app` build configuration without ejecting. ```bash npm install -D react-app-rewired ``` -------------------------------- ### Install Node.js Dependencies Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Installs the necessary Node.js packages for the project, including body-parser, cors, dotenv, express, passport, passport-oauth2, and redis. This command is run using npm within a Node.js project. ```Bash npm install --save body-parser cors dotenv express passport passport-oauth2 redis ``` -------------------------------- ### Interact with Redis using Node.js Redis Client Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Provides a Node.js module to interact with Redis. It includes functions to set and get key-value pairs, establishes a connection to the Redis client, and handles potential client errors. ```javascript const { createClient } = require('redis'); const client = createClient(); const setClient = async (key, value) => { await client.set(key, value); } const getClient = async (key) => { const value = await client.get(key); return value; } (async () => { await client.connect(); })(); client.on('error', err => console.log('Redis Client Error', err)); module.exports = { setClient, getClient }; ``` -------------------------------- ### Inject Server-Side Variables into Custom Script Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md This example shows how to pass server-side variables, such as email address and page ID, from Neto's B@SE template language into your custom JavaScript. These variables are made available globally via the window object. ```html
``` -------------------------------- ### Add History Route with Redis Caching (JavaScript Diff) Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md This code snippet, presented as a diff, adds a new GET route '/history' to an Express.js application. It retrieves store domain and authentication tokens from Redis, fetches orders from the Neto API if the cache has expired, maps the orders, and serves them. If the cache is valid, it serves the cached orders. This enhances performance by reducing direct API calls. ```diff const express = require('express'); const passport = require('passport'); + const client = require('./redis'); + const { getOrders, mapOrders } = require('./neto'); const router = express.Router(); router.get( '/auth/callback', passport.authenticate('neto', { session: false, }), (_req, res) => { res.redirect('/auth/success'); } ); router.get('/auth/success', (req, res) => { res.send('Successfully authenticated!'); }); +router.get('/history', async (req, res) => { + const store_domain = req.get('Origin').replace('https://', ''); + const expiryDate = await client.getClient(`${store_domain}#expiry`); + + // serve new orders + if (new Date() > new Date(expiryDate || 0)) { + const secret = await client.getClient(`${store_domain}#token`); + const json = await getOrders(store_domain, secret); + const orders = mapOrders(json.Order); + res.json(orders); + await client.setClient(`${store_domain}#expiry`, new Date(Date.now() + 5184000000).toISOString()); + await client.setClient(`${store_domain}#orders`, JSON.stringify(orders)); + } + // serve cached orders + else { + const json = await client.getClient(`${store_domain}#orders`); + const orders = JSON.parse(json); + res.json(orders); + } +}); module.exports = router; ``` -------------------------------- ### Initialize Node.js Project Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Initializes a new Node.js project by creating a package.json file. This command is run within a Node.js project directory. ```Bash npm init ``` -------------------------------- ### Create React App for Neto Addon Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Initializes a new React project named 'addon-front-end' using `create-react-app`. This command sets up the basic structure for the front-end widget. ```bash npx create-react-app addon-front-end ``` -------------------------------- ### Create Project Folder Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Creates a new directory for the add-on's backend code. This is a standard command-line operation. ```Bash mkdir addon-back-end ``` -------------------------------- ### Navigate to Project Folder Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Changes the current directory to the newly created project folder. This is a standard command-line operation. ```Bash cd addon-back-end ``` -------------------------------- ### Build Custom Script with npm Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md This command initiates the build process for your custom widget, generating a bundled JavaScript file. The output is typically found in a 'build/static/' directory. ```bash npm run build ``` -------------------------------- ### Configure Build Optimization Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Provides a `config-overrides.js` file to modify the Webpack configuration used by `create-react-app`. It disables runtime chunk and splits chunks to ensure a single output file for the widget. ```javascript module.exports = function override(config) { config.optimization.runtimeChunk = false; config.optimization.splitChunks = { cacheGroups: { default: false, }, }; return config; }; ``` -------------------------------- ### Fetch Neto Orders and Map Data (JavaScript) Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md This snippet defines two asynchronous JavaScript functions: `getOrders` to retrieve order data from the Neto API using POST requests with specific headers and filters, and `mapOrders` to transform the fetched order data into a simpler format. It requires a configuration file for client ID and uses the `fetch` API. Error handling is included for the fetch operation. ```javascript const config = require('./config'); const getOrders = async (store_domain, secret) => { try { const res = await fetch(`https://${store_domain}/do/WS/NetoAPI`, { method: 'POST', headers: { X_ACCESS_KEY: config.CLIENT_ID, X_SECRET_KEY: secret, NETOAPI_ACTION: 'GetOrder', Accept: 'application/json', }, body: `{ "Filter": { "DatePlacedFrom": "${new Date(Date.now() - 86400000).toISOString()}", "OutputSelector": [ "OrderLine", "OrderLine.ProductName", "BillAddress", "DatePlaced" ] } }`, }); orders = await res.json(); return orders; } catch (e) { return `Fetch Error. ${e}`; } }; const mapOrders = (orders) => { return orders.map((order) => ({ date_placed: order.DatePlaced, sku: order.OrderLine[0].SKU, name: order.OrderLine[0].ProductName, city: order.BillCity, })); }; module.exports = { mapOrders, getOrders }; ``` -------------------------------- ### Define Express Routes for Neto OAuth Callback Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Creates Express routes to handle the OAuth callback from Neto and a success route. The callback route uses Passport.js to authenticate the user and redirects to a success page upon successful authentication. ```javascript const express = require('express'); const passport = require('passport'); const router = express.Router(); router.get( '/auth/callback', passport.authenticate('neto', { session: false, }), (_req, res) => { res.redirect('/auth/success'); } ); router.get('/auth/success', (req, res) => { res.send('Successfully authenticated!'); }); module.exports = router; ``` -------------------------------- ### Load Environment Variables for Application Configuration Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Loads environment variables, specifically 'CLIENT_ID' and 'CLIENT_SECRET', using the 'dotenv' package. These variables are intended to store sensitive credentials securely. ```javascript require("dotenv").config(); module.exports = { CLIENT_ID: process.env.CLIENT_ID, CLIENT_SECRET: process.env.CLIENT_SECRET }; ``` -------------------------------- ### Configure Passport OAuth2 Strategy for Neto Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md Sets up a Passport.js OAuth2 strategy for integrating with Neto's authentication system. It defines authorization and token URLs, client credentials, callback URL, and handles the access token by storing it in Redis. ```javascript const passport = require('passport'); const OAuth2Strategy = require('passport-oauth2'); const client = require('./redis'); const config = require('./config'); passport.use( 'neto', new OAuth2Strategy( { authorizationURL: 'https://apps.getneto.com/oauth/v2/auth', tokenURL: 'https://apps.getneto.com/oauth/v2/token', clientID: config.CLIENT_ID, clientSecret: config.CLIENT_SECRET, callbackURL: 'http://localhost:3000/auth/callback', passReqToCallback: true, }, async ( req, accessToken, _refreshToken, { store_domain }, info, callback ) => { client.setClient(`${store_domain}#token`, accessToken); callback(null, {}); } ) ); module.exports = passport; ``` -------------------------------- ### Add Styling and Elapsed Time to React Widget Source: https://github.com/netoecommerce/app-tutorial/blob/master/README.md This code extends the previous React widget by adding inline styles for presentation and a helper function `getElapsedTime` to display how long ago an order was placed. The widget now shows the elapsed time below the purchase details. ```javascript import React, { useEffect, useState } from "react"; const styles = { position: "fixed", bottom: 100, right: 50, width: 400, height: 80, zIndex: 2000, boxShadow: "4px 4px 4px grey", border: "1px solid #fafafa", fontSize: 12, background: "white", padding: 8, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center' }; const getElapsedTime = (time) => { const since = Number(time); const elapsed = Date.now() - since; const second = 1000; const minute = second * 60; const hour = minute * 60; const day = hour * 24; if (elapsed >= second && elapsed < minute) { const seconds = Math.floor(elapsed / second); return `${seconds} second${seconds > 1 ? "s" : ""} ago`; } if (elapsed >= minute && elapsed < hour) { const minutes = Math.floor(elapsed / minute); return `${minutes} minute${minutes > 1 ? "s" : ""} ago`; } if (elapsed >= hour && elapsed < day) { const hours = Math.floor(elapsed / hour); return `${hours} hour${hours > 1 ? "s" : ""} ago`; } const days = Math.floor(elapsed / day); return `${days} day${days > 1 ? "s" : ""} ago`; }; const App = () => { // ... existing code ... const { city, name, date_placed } = activeOrder; return Object.keys(activeOrder).length ? (Someone in {city} bought {name}!
{getElapsedTime(new Date(date_placed))}Someone in {city} bought {name}!