import React from 'react'; import superagent from 'superagent'; import { fromUnixTime, formatDistanceToNow } from "date-fns"; import { formatInTimeZone } from 'date-fns-tz'; import * as ApiUtils from '../CloudApi/ApiUtils.js'; import constants, { DEFAULT_TIMEZONE } from '../CloudApi/Constants.js'; import WaxIdBitField from '../Fabricator/WaxIdBitField.jsx'; import ToolPathDownloadButton from '../Fabricator/ToolPathDownloadButton.jsx'; import ShopifyOrderComponent from './ShopifyOrderComponent.jsx'; import ScanRequestComponent from '../Fabricator/ScanRequestComponent.jsx'; import ScanRequestsTable from '../Fabricator/ScanRequestsTable.jsx'; import ClickableTableRow from '../Util/ClickableTableRow.jsx'; import ApiButtonModal from '../CloudApi/ApiButtonModal.jsx'; import BigApiErrorMessage from '../CloudApi/BigApiErrorMessage.jsx'; import BigShipmentInventoryCards from '../BigLogistics/BigShipmentInventoryCards.jsx'; import BigShipmentInventorySlotsTable from '../BigLogistics/BigShipmentInventorySlotsTable.jsx'; import BigOrderTableCells from './BigOrderTableCells.jsx'; import { Header, Message, Icon, Button, Table, Label, Segment, Modal, Form, Statistic, Radio, Grid, Dropdown, Image, Card, Divider } from 'semantic-ui-react' /** * HACK HACK HACK * * I am splitting this off into a component to prevent an even propagation bug. * * In Semantic UI React, the space bar triggers a click on the Dropdown trigger. */ class BigShipmentHoldButtonModal extends React.PureComponent { constructor(props) { super(props); this.state = { shipmentHoldReason: "", shipmentHoldNotes: "", }; this.onChange = this.onChange.bind(this); this.onConfirm = this.onConfirm.bind(this); } onChange(e, { name, value }) { this.setState({ [name]: value }) } onConfirm() { this.props.onConfirm(this.state.shipmentHoldReason, this.state.shipmentHoldNotes); } handleKeyDown(event) { if (event.key === ' ') { event.stopPropagation(); } } render() { return ( This will add a hold to the order and will prevent anyone from shipping it via BigShipper.
); } } export default class BigOrderComponent extends React.Component { constructor(props) { super(props); this.state = { loadingChecklist: false, showNextActionModal: false, showCreateJobModal: false, showIPDScanRequestModal: false, priority: 0, inventorySlots: [], shipmentHoldNotes: "", shipmentHoldReason: "", expandedHistoryGroups: {}, // Track which repeated message groups are expanded }; this.onChange = this.onChange.bind(this); this.toggleHistoryGroup = this.toggleHistoryGroup.bind(this); } toggleHistoryGroup(groupIndex) { this.setState(prevState => ({ expandedHistoryGroups: { ...prevState.expandedHistoryGroups, [groupIndex]: !prevState.expandedHistoryGroups[groupIndex] } })); } async componentDidMount() { this.setState({ newShippingServiceLevel: this.props.bigOrder.shippingServiceLevel, newPriority: this.props.bigOrder.priority, newIpd: this.props.bigOrder.ipd }); if (!this.props.asTableRow) { await this.onLoadInventory(); } } async onUpdateChecklist() { this.setState({ loadingChecklist: true }); try { let res = await superagent.get(`/api/admin/shop/order/${this.props.bigOrder.id}/checklist`); } catch (e) { if (e.status === 422) { this.props.bigOrder.checklistResult = e.response.body; } } if (!this.props.asTableRow) { window.location.href = `/shop/order/${this.props.bigOrder.id}`; } } async onSynchronizeLineItems() { this.setState({ loading: true }); try { let res = await superagent.get(`/api/admin/shop/order/${this.props.bigOrder.id}/sync_line_items`); } catch (e) { if (e.status === 422) { this.props.bigOrder.checklistResult = e.response.body; } } if (!this.props.asTableRow) { window.location.href = `/shop/order/${this.props.bigOrder.id}`; } } async onCreateShipment(shipmentGroupId) { this.setState({ loading: true }); try { console.log("Creating shipment for group", shipmentGroupId); let res = await superagent.post(`/api/admin/shipper/shipment`).send({ bigOrderId: this.props.bigOrder.id, shipmentGroupId }); window.location.href = `/shipper/shipment/${res.body.id}`; } catch (e) { console.log(e); } this.setState({ loading: false }); } async onSynchronizeBigOrder() { this.setState({ loading: true }); try { let res = await superagent.get(`/api/admin/shop/order/${this.props.bigOrder.id}/sync`); window.location.href = `/shop/order/${this.props.bigOrder.id}`; } catch (e) { console.log(e); } this.setState({ loading: false }); } async onDeleteBigOrder() { this.setState({ loading: true }); try { const result = await superagent.delete(`/api/admin/shop/order/${this.props.bigOrder.id}`).accept('json'); } catch (e) { console.log(e); } window.location.href = `/shop/orders`; this.setState({ loading: false }); } onChange(e, { name, value }) { e.stopPropagation(); e.preventDefault(); this.setState({ [name]: value }) } showNextActionModal(i, e) { this.setState({ showNextActionModal: true }); } closeNextActionModal(i, e) { this.setState({ showNextActionModal: false }); } async onNextAction(e) { e.preventDefault(); if (!this.props.bigOrder.checklistResult) { return; } this.setState({ loading: true, showNextActionModal: false }); try { let res = await superagent.put(`/api/admin/shop/order/${this.props.bigOrder.id}`).send({ action: this.props.bigOrder.checklistResult.nextAction }); this.props.bigOrder.checklistResult.nextAction = null; if (!this.props.asTableRow) { window.location.href = `/shop/order/${this.props.bigOrder.id}`; } } catch (e) { window.alert("Error sending scan request email"); console.error(e); } this.setState({ loading: false }); } showCreateJobModal() { this.setState({ priority: 0, showCreateJobModal: true }); } closeCreateJobModal(i, e) { this.setState({ showCreateJobModal: false, error: null }); } async onCreateJob(e) { e.preventDefault(); this.setState({ loading: true }); try { let res = await superagent.put(`/api/admin/shop/order/${this.props.bigOrder.id}`).send({ priority: this.state.priority, action: this.props.bigOrder.checklistResult.nextAction }); window.location.href = `/shop/order/${this.props.bigOrder.id}`; } catch (e) { window.alert("Error sending scan request email"); console.error(e); } this.setState({ loading: false, showNextActionModal: false }); } async onCreateReturnLabel(shipmentId) { this.setState({ loading: true }); try { let res = await superagent.put(`/api/admin/shipper/shipment/${shipmentId}`).send({ action: "AdminCreateReturnLabel" }); this.setState({ returnLabel: res.body, loading: false }); } catch (e) { this.setState({ returnLabelError: e }); console.error(e); } this.setState({ loading: false }); } async onRegenerateScanRequest(e) { this.setState({ loading: true }); try { let res = await superagent.put(`/api/admin/shop/order/${this.props.bigOrder.id}`).send({ action: "AdminRegenerateScanRequest" }); window.location.href = `/shop/order/${this.props.bigOrder.id}`; } catch (e) { window.alert("Error sending scan request email"); console.error(e); } this.setState({ loading: false }); } async onResendScanResultEmail() { this.setState({ loading: true }); try { let res = await superagent.put(`/api/admin/shop/order/${this.props.bigOrder.id}`).send({ action: "ResendScanResultEmail" }); window.location.href = `/shop/order/${this.props.bigOrder.id}`; this.setState({ loading: false }); } catch (ex) { console.log(ex.status); console.log(ex.response.body); this.setState({ error: ex.response.body.message, loading: false }); console.error(ex); } } async onAdminOverrideIpd() { this.setState({ loading: true }); try { let res = await superagent.put(`/api/admin/shop/order/${this.props.bigOrder.id}`).send({ action: "AdminOverrideIPD", ipd: this.state.newIpd }); window.location.href = `/shop/order/${this.props.bigOrder.id}`; this.setState({ loading: false }); } catch (ex) { console.log(ex.status); console.log(ex.response.body); this.setState({ error: ex.response.body.message, loading: false }); console.error(ex); } } async onAdminAddShippingHold(shipmentHoldReason, shipmentHoldNotes) { this.setState({ loading: true }); try { let res = await superagent.put(`/api/admin/shop/order/${this.props.bigOrder.id}`).send({ action: "AdminAddShippingHold", shipmentHold: { reason: shipmentHoldReason, notes: shipmentHoldNotes } }); window.location.href = `/shop/order/${this.props.bigOrder.id}`; } catch (ex) { console.log(ex.status); console.log(ex.response.body); this.setState({ error: ex.response.body.message }); console.error(ex); } this.setState({ loading: false }); } async onAdminRemoveShippingHold() { this.setState({ loading: true }); try { let res = await superagent.put(`/api/admin/shop/order/${this.props.bigOrder.id}`).send({ action: "AdminRemoveShippingHold" }); window.location.href = `/shop/order/${this.props.bigOrder.id}`; } catch (ex) { console.log(ex.status); console.log(ex.response.body); this.setState({ error: ex.response.body.message }); console.error(ex); } this.setState({ loading: false }); } async onAdminUpdatePriority() { this.setState({ loading: true }); try { let res = await superagent.put(`/api/admin/shop/order/${this.props.bigOrder.id}`).send({ action: "AdminSetPriority", priority: this.state.newPriority }); window.location.href = `/shop/order/${this.props.bigOrder.id}`; } catch (ex) { console.log(ex.status); console.log(ex.response.body); this.setState({ error: ex.response.body.message }); console.error(ex); } this.setState({ loading: false }); } async onAdminSetShippingServiceLevel() { this.setState({ loading: true }); try { let res = await superagent.put(`/api/admin/shop/order/${this.props.bigOrder.id}`).send({ action: "AdminSetShippingServiceLevel", shippingServiceLevel: this.state.newShippingServiceLevel }); window.location.href = `/shop/order/${this.props.bigOrder.id}`; } catch (ex) { console.log(ex.status); console.log(ex.response.body); this.setState({ error: ex.response.body.message }); console.error(ex); } this.setState({ loading: false }); } async onLoadInventory() { this.setState({ loading: true }); try { let res = await superagent.get(`/api/admin/shop/order/${this.props.bigOrder.id}/shipping_summary`); console.log(res.body); this.setState({ shipping_summary: res.body }); let res2 = await superagent.get(`/api/admin/shop/order/${this.props.bigOrder.id}/shipments`); this.setState({ shipments: res2.body.items, loading: false }); } catch (ex) { console.log(ex.status); console.log(ex.response.body); this.setState({ error: ex.response.body.message, loading: false }); console.error(ex); } } renderReturnLabelModal(shipmentId) { let modalContent = null; if (this.state.returnLabel) { const shippingLabel = this.state.returnLabel; const trackingNumber = shippingLabel.tracking_number || shippingLabel.trackingNumber; const trackingUrlProvider = shippingLabel.tracking_url_provider || shippingLabel.trackingUrlProvider; const uploadLabelUrl = shippingLabel.upload_label_url || shippingLabel.uploadLabelUrl; const rateAmount = shippingLabel.rate_amount || shippingLabel.rateAmount; const rateCurrency = shippingLabel.rate_currency || shippingLabel.rateCurrency || "USD"; const originalShipment = this.state.shipments?.find(s => s.id === shipmentId); const originalShippingLabel = originalShipment?.currentShippingLabel; const originalTrackingNumber = originalShippingLabel?.tracking_number || originalShippingLabel?.trackingNumber; const originalTrackingUrl = originalShippingLabel?.tracking_url_provider || originalShippingLabel?.trackingUrlProvider; modalContent = (
Copy the tracking number and pdf link into Shopify...
Return Tracking #
{trackingNumber}
{trackingUrlProvider && Return Tracking Link
{trackingUrlProvider}
} Return PDF File Link
{uploadLabelUrl}
{rateAmount && Return Label Cost
{rateCurrency === "USD" ? "US" : rateCurrency}${parseFloat(rateAmount).toFixed(2)}
} Order
{this.props.bigOrder.shopifyOrderName} — {this.props.shopifyOrder?.customer?.first_name} {this.props.shopifyOrder?.customer?.last_name}
{originalTrackingNumber && Original Tracking #
{originalTrackingNumber}
} {originalTrackingUrl && Original Tracking Link
{originalTrackingUrl}
}
); } else { modalContent = ( {this.state.loading && } Create Return Label? This will take several seconds... ); } return ( {this.state.returnLabelError && } {modalContent} ); } renderTitle() { const createdAtDateTime = Date.parse(this.props.shopifyOrder.created_at); const created = formatInTimeZone(createdAtDateTime, DEFAULT_TIMEZONE, 'PPPp z'); const title = (
📦 Order {this.props.bigOrder.shopifyOrderName} for {this.props.shopifyOrder.customer.first_name} {this.props.shopifyOrder.customer.last_name}
); const createdAt = formatInTimeZone(createdAtDateTime, DEFAULT_TIMEZONE, 'PPPp z'); const fromNow = formatDistanceToNow(createdAtDateTime, { addSuffix: true }); let shopifyButton = (this.props.bigOrder.origin === "Shopify") ? : null const orderActionsAllowed = this.props.bigOrder.state !== "Shipped" && this.props.bigOrder.state !== "Cancelled"; return (
{title}

Created: {createdAt} ({fromNow})

📦 Big Order Actions... }> {orderActionsAllowed && <> {!this.props.bigOrder.shipmentHold && } This will update the order's priority -- higher priority will be shipped sooner.
This will force the customer's IPD to be updated.
Admin only. Changing this option may cause BigShipper to use a faster shipping option, if available.

The following will happen when you resend a scan request:
  • All existing scan requests for this customer will be revoked.
  • A new scan request will be generated for this customer.
  • An email will be sent to the customer with a new link for them to start the scanning process.
  • Any jobs attached to this order will be marked as "destroyed".
  • The order status will be set to .
This will send a link so the customer can change their IPD.
  • This will only work if the customer has already completed a scan request.
} This will delete the Big Order. {shopifyButton}
); } renderBanners() { const banners = []; if (this.props.shopifyOrder.THIS_SHOPIFY_ORDER_WAS_DELETED) { banners.push( The underlying shopify order was deleted ) } if (this.props.shopifyOrder.tags.includes("test order") || this.props.shopifyOrder.test === true) { banners.push( This is a test order. BE CAREFUL WITH SHIPPING THIS ORDER! ); } return <> {banners} ; } renderShopifyData() { return (
Shopify Data {(this.props.shopifyOrder.THIS_SHOPIFY_ORDER_WAS_DELETED) && }
); } renderChecklist() { if (this.state.loadingChecklist) { return ( Checking order status... ) } else if (this.props.bigOrder.checklistResult) { let currentState = this.props.bigOrder.state; let nextAction = this.props.bigOrder.nextAction; let nextActionMessage = this.props.bigOrder.checklistResult.message; let nextActionText = nextAction; if (nextAction === "SendScanRequest") { nextActionText = "Send scan request email to customer"; } else if (nextAction === "SendScanRequestReminder") { nextActionText = "Send a scan request reminder email to customer"; } let shipmentsList = null; if (this.props.shipments.length > 0) { shipmentsList = ( <> {this.props.shipments.map((shipment, shipmentIndex) => { let color = "green"; if (shipment.status === "Cancelled") { color = "red"; } return (
); } )} ); } let nextActionComponent = null; let segmentColor = "green"; let icon = "warning circle"; if (nextAction === "SendScanRequest") { segmentColor = "red"; nextActionComponent = ; } else if (nextAction === "SendScanRequestReminder") { segmentColor = "purple"; nextActionComponent = ; } else if (nextAction === "CreateJob") { segmentColor = "blue"; nextActionComponent = ; } else if (nextAction === "Shipped") { segmentColor = "green"; icon = "checkmark"; nextActionComponent = shipmentsList; } else { nextActionComponent = shipmentsList; } nextActionText = currentState; if (this.props.showDetail) { return ( Status: {nextActionText} {nextActionMessage} {nextActionComponent} ); } else if (nextAction !== "Shipped") { if (nextActionComponent) { return (
Status: {nextActionText} {nextActionComponent}
); } } } return null; } renderShipment() { if (this.props.bigOrder && this.props.bigOrder.state === "Shipped") { return null; } if (this.state.loading) { return ( Loading shipments... ) } if (this.props.bigOrder.shipmentHold) { return ( There is a Hold on this order preventing shipment!

Reason: {this.props.bigOrder.shipmentHold.reason}

Notes: {this.props.bigOrder.shipmentHold.notes}

This will remove the hold on the order and allow it to be shipped via BigShipper.
); } if (this.props.bigOrder.state === "Cancelled") { return ( This order has been cancelled! ); } const fulfilledShipments = this.props.shipments.filter(shipment => ["WaitingForPickup", "InTransit", "Shipped", "DeliveryFailed"].includes(shipment.status)); if (this.state.shipping_summary) { // HACK HACK HACK // // "null" returned as a string. Find out why this is. const shipmentGroupIds = Object.keys(this.state.shipping_summary.shipmentGroups).filter(id => id != "null"); let inventorySummary = shipmentGroupIds.map((shipmentGroupId, i) => { let shipment = this.state.shipping_summary.shipmentGroups[shipmentGroupId].shipment; let inventorySlots = this.state.shipping_summary.shipmentGroups[shipmentGroupId].inventorySlots; let header = Shipment #{i + 1}; if (inventorySlots) { let availableSlots = inventorySlots.filter(slot => slot.status === "StockAvailable"); let inventoryStatusModal = ( {this.state.loading && } {(!this.state.loading && inventorySlots) &&
{`${availableSlots.length} out of ${inventorySlots.length} Items In Stock`}
} {(!this.state.loading && inventorySlots) && }
); if (availableSlots.length > 0 && availableSlots.length === inventorySlots.length) { return ( {header} Shipment can be completed now: {inventoryStatusModal} ); } else { return ( {header} Not enough inventory to ship: {inventoryStatusModal} ); } } else if (shipment) { let color = "green"; return ( {header} This shipment has already been created: {this.state.loading && } {(!this.state.loading) &&
This shipment is {shipment.status}
} {(!this.state.loading) && }
); } }); return
Shippability
{inventorySummary}
; } else { return null; } } renderPendingScanRequests() { if (this.state.loadingChecklist) { return ( Loading scan requests... ) } else if (this.props.scanRequests) { const hasScanRequests = this.props.scanRequests.length > 0; if (hasScanRequests) { return ( ); } } return null; } /** * Groups consecutive history items with the same message into collapsed groups. * Returns an array of groups, where each group has: * - items: array of history items in the group * - message: the shared message * - count: number of items in the group */ groupHistoryItems(history) { if (!history || history.length === 0) { return []; } const groups = []; let currentGroup = null; for (const item of history) { if (currentGroup && currentGroup.message === item.message) { // Same message as current group, add to it currentGroup.items.push(item); currentGroup.count++; } else { // Different message, start a new group if (currentGroup) { groups.push(currentGroup); } currentGroup = { items: [item], message: item.message, count: 1 }; } } // Don't forget the last group if (currentGroup) { groups.push(currentGroup); } return groups; } renderBigOrderStuff() { if (!this.props.bigOrder) { return null; } // HACK HACK HACK - hard coding here. const looperAccountId = "cc1d57a8-1351-4581-8350-b067dd504791"; // Group consecutive repeated messages const historyGroups = this.groupHistoryItems(this.props.bigOrder.history); const bigOrderHistory = historyGroups.flatMap((group, groupIndex) => { if (group.count === 1) { // Single item, render normally const historyItem = group.items[0]; const accountName = (historyItem.bigscreenAccountId === looperAccountId) ? "factory_looper" : `${historyItem.bigscreenAccountId.substring(0, 8)}`; let createdAtDateTime = fromUnixTime(historyItem.createdAt / 1000); const createdAt = formatInTimeZone(createdAtDateTime, DEFAULT_TIMEZONE, 'PPPp z'); return [( {createdAt} {accountName} {historyItem.message} )]; } else { // Multiple items with same message - show collapsed or expanded const isExpanded = this.state.expandedHistoryGroups[groupIndex]; const firstItem = group.items[0]; const lastItem = group.items[group.items.length - 1]; if (isExpanded) { // Show all items in the group with an option to collapse return group.items.map((historyItem, itemIndex) => { const accountName = (historyItem.bigscreenAccountId === looperAccountId) ? "factory_looper" : `${historyItem.bigscreenAccountId.substring(0, 8)}`; let createdAtDateTime = fromUnixTime(historyItem.createdAt / 1000); const createdAt = formatInTimeZone(createdAtDateTime, DEFAULT_TIMEZONE, 'PPPp z'); return ( this.toggleHistoryGroup(groupIndex)} > {createdAt} {itemIndex === 0 && } e.stopPropagation()}>{accountName} {historyItem.message} ); }); } else { // Show collapsed row let firstCreatedAtDateTime = fromUnixTime(firstItem.createdAt / 1000); let lastCreatedAtDateTime = fromUnixTime(lastItem.createdAt / 1000); const firstCreatedAt = formatInTimeZone(firstCreatedAtDateTime, DEFAULT_TIMEZONE, 'PPPp z'); const lastCreatedAt = formatInTimeZone(lastCreatedAtDateTime, DEFAULT_TIMEZONE, 'PPPp z'); const firstAccountName = (firstItem.bigscreenAccountId === looperAccountId) ? "factory_looper" : `${firstItem.bigscreenAccountId.substring(0, 8)}`; return [( this.toggleHistoryGroup(groupIndex)} > {firstCreatedAt}
→ {lastCreatedAt}
e.stopPropagation()}>{firstAccountName} {group.message}  
)]; } } }); let createdAtDateTime = fromUnixTime(this.props.bigOrder.createdAt / 1000); const createdAt = formatInTimeZone(createdAtDateTime, DEFAULT_TIMEZONE, 'PPPp z'); const fromNow = formatDistanceToNow(createdAtDateTime, { addSuffix: true }); return (
ID {this.props.bigOrder.id}  Created {createdAt} ({fromNow}) IPD {this.props.bigOrder.ipd === -1 ? "Not yet set" : `${this.props.bigOrder.ipd}mm (From ${this.props.bigOrder.ipdSource})`} Version {this.props.bigOrder.version} Priority {this.props.bigOrder.priority} Shipping Service Level {this.props.bigOrder.shippingServiceLevel}
Big Order History
When User Message {bigOrderHistory}
) } renderLineItemsForShipmentGroup(shipmentGroupId, lineItems, color="blue") { return lineItems.map(lineItem => { if (lineItem.type === this.props.schemas.BigProductTypes.Other) { return null; } if (lineItem.shipmentGroupId !== shipmentGroupId) { return null; } let title = lineItem.type; let variantTitle = "Unknown Variant"; let sku = lineItem.shopifySku; let quantity = 1; let imageSrc = `/images/${lineItem.type}.png`; const shopifyLineItem = lineItem.shopifyOrderLineItemSnapshot; if (shopifyLineItem && shopifyLineItem.product_id && shopifyLineItem.variant_id) { quantity = shopifyLineItem.quantity; const product = this.props.products.find(product => product.id === shopifyLineItem.product_id); if (product) { title = product.title; imageSrc = product.image.src; } const variant = product.variants.find(variant => variant.id === shopifyLineItem.variant_id); if (variant) { variantTitle = variant.title; // Get the image for the variant from the product - need to look up the images array on the product const variantImage = product.images.find(image => image.id === variant.image_id); if (variantImage) { imageSrc = variantImage.src; } } } const lineItemData =
{quantity}x
; if (lineItem.type === this.props.schemas.BigProductTypes.PrescriptionLenses) { return (
{title}
{lineItemData}

OS Left: {lineItem.OSLeft}

OD Right: {lineItem.ODRight}

); } if (lineItem.type === this.props.schemas.BigProductTypes.CustomCushion || lineItem.type === this.props.schemas.BigProductTypes.BeyondCushionV1 || lineItem.type === this.props.schemas.BigProductTypes.ReplacementBeyondCushionV1) { const jobInfo = (_.isEmpty(lineItem.currentJobId)) ? : ; return (
{title}
{lineItemData}
Current Job: {jobInfo}
) } if (lineItem.type === this.props.schemas.BigProductTypes.UniversalLightFitSeal) { title = "Universal Lightfit Seal"; variantTitle = "AKA BS1U"; } return (
{title} ({variantTitle})
{lineItemData}
); }); } renderLineItemsV3() { if (!this.props.schemas) { return null; } if (!this.props.products) { return null; } if (!this.state.shipping_summary) { return null; } let unfulfilledLineItems = []; let fulfilledLineItems = []; let refundedLineItems = []; let lineItemSummary = this.state.shipping_summary.lineItemSummary; let unfulfilledTitle = "Pending Shipments"; if (lineItemSummary) { fulfilledLineItems = lineItemSummary.lineItems.filter(lineItem => lineItemSummary.fulfilledLineItemIds.includes(lineItem.shopifyOrderLineItemId)); unfulfilledLineItems = lineItemSummary.lineItems.filter(lineItem => lineItemSummary.unfulfilledLineItemIds.includes(lineItem.shopifyOrderLineItemId)); refundedLineItems = lineItemSummary.lineItems.filter(lineItem => lineItemSummary.refundedLineItemIds.includes(lineItem.shopifyOrderLineItemId)); } else { unfulfilledLineItems = this.props.bigOrder.currentLineItems; unfulfilledTitle = "Shipments"; } let components = []; if (unfulfilledLineItems.length > 0) { const shipmentGroupIds = _.uniq(unfulfilledLineItems.map((lineItem) => lineItem.shipmentGroupId)); components.push((
{unfulfilledTitle} ({shipmentGroupIds.length})
  {shipmentGroupIds.map((shipmentGroupId, shipmentGroupIndex) => { const lineItems = this.renderLineItemsForShipmentGroup(shipmentGroupId, unfulfilledLineItems, "white"); if (_.isEmpty(lineItems)) { return null; } return (

Shipment {shipmentGroupIndex + 1}
  {lineItems}
); })}
)); } if (this.state.shipments && this.state.shipments.length > 0 && fulfilledLineItems.length > 0) { const shipmentGroupIds = _.uniq(this.state.shipments.filter(shipment => shipment.status != "Cancelled").map((shipment) => shipment.shipmentGroupId)); components.push((
Completed Shipments ({shipmentGroupIds.length})
{shipmentGroupIds.map((shipmentGroupId, shipmentGroupIndex) => { const lineItems = this.renderLineItemsForShipmentGroup(shipmentGroupId, fulfilledLineItems, "blue"); if (_.isEmpty(lineItems)) { return null; } // Find the shipment that corresponds to this shipmentGroupId const shipment = this.state.shipments.find(s => s.shipmentGroupId === shipmentGroupId && (s.status === "Shipped" || s.status === "WaitingForPickup")); // Only show return label button for Shipped or WaitingForPickup shipments let returnLabelButton = null; if (shipment) { returnLabelButton = ( {this.renderReturnLabelModal(shipment.id)} ); } return (

Shipment {shipmentGroupIndex + 1}
{returnLabelButton} {lineItems}
); })}
)); } if (refundedLineItems.length > 0) { components.push((
({refundedLineItems.length} Refunded or Cancelled items hidden)
)); } return <>{components.map((component, index) => {component})}; } renderNextActionModal() { return (
Send a scan request email to this user? This action may take several seconds. ) } renderCreateJobModal() { return (
This step will create a job for this order. This action may take several seconds while the job is created and the scan verified.

Set an optional priority for the job

) } render() { if (!this.props.bigOrder) { return null; } if (this.props.asTableRow) { let urlOrderId = this.props.bigOrder.id; if (this.props.bigOrder.shopifyOrderName) { urlOrderId = this.props.bigOrder.shopifyOrderName; } return ( ); } else { if (!this.props.shopifyOrder) { return null; } return (
{this.renderNextActionModal()} {this.renderCreateJobModal()} {this.renderBanners()} {this.renderTitle()} {this.renderShipment()} {this.renderChecklist()} {this.renderBigOrderStuff()} {this.renderPendingScanRequests()} {this.renderShopifyData()} {(this.props.showDetail) && this.renderLineItemsV3()}
); } } }