Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1e11b3b
feat(orders): ORDERS-7706 add new return page layout
jpajar May 4, 2026
2bbd8d8
feat(returns): ORDERS-7704 condition for new returns on orders page (…
bc-vivekaggarwal May 12, 2026
246d633
Merge pull request #2649 from bigcommerce/ORDERS-7706
jpajar May 8, 2026
1d7375a
feat(returns): ORDERS-7705 condition for new returns on order details…
bc-vivekaggarwal May 12, 2026
01392b8
Merge pull request #2649 from bigcommerce/ORDERS-7706
jpajar May 8, 2026
b5a0c9b
feat(orders): ORDERS-7734 add template for guest return portal
BC-SEven Apr 28, 2026
8498823
feat(returns): ORDERS-7717 - add new returns list ui
bc-bryan May 26, 2026
25cb72f
feat(returns): ORDERS-7717 - use rma number
bc-bryan May 27, 2026
ebc2fce
feat(orders): ORDERS-7734 add template for guest return portal
BC-SEven Apr 28, 2026
9aeda72
feat(returns): ORDERS-7708 new-return page — render items from order …
bc-vivekaggarwal May 27, 2026
e79ca2d
feat(orders): ORDERS-7331 add template for create return page
May 28, 2026
2737dbb
feat(returns): ORDERS-7717 - add CHANGELOG item
bc-bryan May 28, 2026
ac768ab
feat(returns): ORDERS-7717 - address copilot review
bc-bryan May 28, 2026
eb70fd3
Merge pull request #2669 from bigcommerce/ORDERS-7331
matt-evangelidis May 29, 2026
e51401e
Merge pull request #2664 from bigcommerce/ORDERS-7717-returns-pages-v2
bc-bryan Jun 2, 2026
5d1dc06
feat(orders): ORDERS-7718 add return details page layout
jpajar Jun 5, 2026
7c5e352
feat(orders): ORDERS-7718 add status mappping from enum
jpajar Jun 9, 2026
6ebc73c
Merge pull request #2675 from bigcommerce/ORDERS-7718
jpajar Jun 9, 2026
a12175b
Orders 7764 render create return page with context (#2673)
bc-vivekaggarwal Jun 11, 2026
372a2e0
feat(orders): ORDERS-7707 improve create return page design for mobil…
jpajar Jun 16, 2026
621d2ce
Merge pull request #2682 from bigcommerce/ORDERS-7707_2
jpajar Jun 17, 2026
a323338
feat(orders): ORDERS-7715 add createReturn mutation and confirmation …
jpajar Jun 22, 2026
cc8b5ed
feat(orders): ORDERS-7715 handle double submissions
jpajar Jun 22, 2026
bcea92e
Merge pull request #2685 from bigcommerce/ORDERS-7715
jpajar Jun 23, 2026
3aa1d8b
feat(orders): ORDERS-7859 add error message on failed return creation
jpajar Jun 23, 2026
b731639
Merge pull request #2686 from bigcommerce/ORDERS-7859
jpajar Jun 23, 2026
d432f2e
feat(returns): ORDERS-7857 add loading state as return submission (#2…
bc-vivekaggarwal Jun 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Draft
- Add new create-return page [#2669](https://github.com/bigcommerce/cornerstone/pull/2669)
- Fix duplicate `id="default_instrument"` on Update Payment Method page [#2661](https://github.com/bigcommerce/cornerstone/pull/2661)
- Respect `available_to_sell` on PDP so the Sold Out alert is hidden and the Add to Cart button stays enabled for backorderable products, and is disabled when quantity exceeds `available_to_sell` [#2659](https://github.com/bigcommerce/cornerstone/pull/2659)
- Updated accessibility features [2656](https://github.com/bigcommerce/cornerstone/pull/2656)
- Adds new guest-return-portal page. [2645](https://github.com/bigcommerce/cornerstone/pull/2645)
- Update 'Ship to' copy for multi address orders [#2655](https://github.com/bigcommerce/cornerstone/pull/2655)
- Fixed typo in category page reset-filters live region handler [#2643](https://github.com/bigcommerce/cornerstone/pull/2643)
- Swap content/data keys in onProductOptionsChanged event detail [#2640](https://github.com/bigcommerce/cornerstone/pull/2640)
- Gate Order Details "Return" button on `settings.returns_v2_enabled` with `settings.returns_enabled` fallback (ORDERS-7705)
- Fix shipment info showing for cancelled orders [#2654](https://github.com/bigcommerce/cornerstone/pull/2654)
- Render backorder prompts on My Account "Your orders" page [#2650](https://github.com/bigcommerce/cornerstone/pull/2650)
- Gate account orders list "Return Items" link on returns settings (ORDERS-7704) [#2653](https://github.com/bigcommerce/cornerstone/pull/2653)
- Added new Return list view (ORDERS-7717) [#2664] (https://github.com/bigcommerce/cornerstone/pull/2664)

## 6.19.1 (04-09-2026)
- Update stencil-utils versionto 6.23.0 [#2638](https://github.com/bigcommerce/cornerstone/pull/2638)
Expand Down
3 changes: 2 additions & 1 deletion assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ __webpack_public_path__ = window.__webpack_public_path__; // eslint-disable-line
import Global from './theme/global';

const getAccount = () => import('./theme/account');
const getAddReturnNew = () => import('./theme/add-return-new');
const getLogin = () => import('./theme/auth');
const noop = null;

Expand All @@ -11,7 +12,7 @@ const pageClasses = {
account_order: getAccount,
account_addressbook: getAccount,
shippingaddressform: getAccount,
account_new_return: getAccount,
account_new_return: getAddReturnNew,
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
'add-wishlist': () => import('./theme/wishlist'),
Comment on lines 12 to 17
account_recentitems: getAccount,
account_downloaditem: getAccount,
Expand Down
93 changes: 93 additions & 0 deletions assets/js/theme/add-return-new.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import PageManager from './page-manager';

export default class AddReturnNew extends PageManager {
onReady() {
const $form = $('[data-new-return-form]');
if (!$form.length) return;

this.bindOrderLineItemEvents();
this.bindSubmit($form);
}

bindOrderLineItemEvents() {
document.querySelectorAll('.newReturn-stepperBtn').forEach(button => {
button.addEventListener('click', () => {
// Derive itemId from the parent row — buttons do not carry data-item-id,
// so the [data-item-id] selector stays scoped to the row container only.
const row = button.closest('.newReturn-orderLineItem');
const itemId = row?.dataset?.itemId;
if (!itemId) return;
const action = button.getAttribute('data-action');
const quantityInput = document.getElementById(`qty-${itemId}`);
// max is set server-side to returnableQuantity → quantity fallback
const maxQty = parseInt(quantityInput.max, 10) || 0;
let quantity = parseInt(quantityInput.value, 10) || 0;

if (action === 'inc' && quantity < maxQty) quantity++;
else if (action === 'dec' && quantity > 0) quantity--;

quantityInput.value = quantity;
const stepper = button.closest('.newReturn-stepper');
stepper.querySelector('[data-action="dec"]').disabled = quantity === 0;
stepper.querySelector('[data-action="inc"]').disabled = quantity >= maxQty;

this.updateSubmitState();
});
});

document.querySelectorAll('.newReturn-select').forEach(selectElement => {
selectElement.addEventListener('change', () => this.updateSubmitState());
});

// Disable + button on load for any item where max=0 (non-returnable)
document.querySelectorAll('.newReturn-stepperInput').forEach(input => {
const maxQty = parseInt(input.max, 10) || 0;
const incBtn = input.closest('.newReturn-stepper')?.querySelector('[data-action="inc"]');
if (incBtn && maxQty === 0) incBtn.disabled = true;
});
}

updateSubmitState() {
const selectedItems = this.getSelectedItems();
const isValid = selectedItems.length > 0 && selectedItems.every(itemRow => {
const itemId = itemRow.dataset?.itemId;
if (!itemId) return false;
const resolutionEl = document.getElementById(`resolution-${itemId}`);
const reasonEl = document.getElementById(`reason-${itemId}`);
return resolutionEl && resolutionEl.value && reasonEl && reasonEl.value;
});

document.getElementById('return-new-submitBtn').disabled = !isValid;
}

// Returns only row containers (not buttons) with a non-zero quantity selected.
getSelectedItems() {
return [...document.querySelectorAll('.newReturn-orderLineItem')].filter(itemRow => {
const itemId = itemRow.dataset?.itemId;
if (!itemId) return false;
const qtyInput = document.getElementById(`qty-${itemId}`);
return qtyInput && parseInt(qtyInput.value, 10) > 0;
});
}

bindSubmit($form) {
$form.on('submit', event => {
event.preventDefault();

const orderEntityId = parseInt(this.context.order?.id, 10);
const additionalNote = document.querySelector('[data-new-return-note]')?.value || '';
const items = this.getSelectedItems().flatMap(itemRow => {
const itemId = itemRow.dataset?.itemId;
if (!itemId) return [];
return [{
lineItemEntityId: parseInt(itemId, 10),
quantity: parseInt(document.getElementById(`qty-${itemId}`)?.value, 10),
resolution: document.getElementById(`resolution-${itemId}`)?.value,
reasonEntityId: document.getElementById(`reason-${itemId}`)?.value,
}];
});

// TODO ORDERS-7715: invoke createReturn Storefront GQL mutation.
});
}
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
}
189 changes: 189 additions & 0 deletions assets/js/theme/return-details.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import { escape } from 'lodash';
import PageManager from './page-manager';

export default class ReturnDetails extends PageManager {
onReady() {
const root = document.querySelector('[data-return-details]');
if (!root) return;

this.root = root;

// ---------------------------------------------------------------------------
// Hardcoded page data which mirrors the returns-ui Return / Item interface
// Replace with the relevant stencil page context when it is surfaced.
// ---------------------------------------------------------------------------
this.pageData = {
id: '101',
rma: '101',
status: 'OPEN',
opened: 'January 1, 2024',
lastUpdated: 'January 1, 2024',
order: {
id: '222',
currency: 'USD',
},
shipping: {
address: {
fullName: 'John Doe',
address1: '1000 San Marcos Ave',
city: 'Austin',
state: 'TX',
zip: '78702',
country: 'United States',
},
method: 'UPS Ground',
trackingNumber: '1Z370170375602560',
dateShipped: 'May 15, 2024',
},
items: [
{
id: 'i1',
lineItemId: 'li1',
name: 'Product Name',
variant: 'Color: Blue · Size: Large',
thumbnailUrl: 'https://via.placeholder.com/72',
sku: 'SKU-001',
requestedResolution: { type: 'system', label: 'Exchange' },
requestedReason: 'Wrong item received',
quantity: 1,
formatted_price: '$123.99',
status: 'open',
},
{
id: 'i2',
lineItemId: 'li2',
name: 'Product Name',
variant: 'Color: Blue · Size: Large',
thumbnailUrl: 'https://via.placeholder.com/72',
sku: 'SKU-002',
requestedResolution: { type: 'system', label: 'Refund' },
requestedReason: 'Did not like',
quantity: 1,
formatted_price: '$123.99',
status: 'open',
},
{
id: 'i3',
lineItemId: 'li3',
name: 'Product Name',
variant: 'Color: Blue · Size: Large',
thumbnailUrl: 'https://via.placeholder.com/72',
sku: 'SKU-003',
requestedResolution: { type: 'system', label: 'Refund' },
requestedReason: 'Did not like',
quantity: 1,
formatted_price: '$123.99',
status: 'open',
},
],
};

this.renderHeader();
this.renderShipping();
this.renderItems();
this.renderSummary();
}

renderHeader() {
const { rma, status } = this.pageData;
const titleEl = this.root.querySelector('[data-return-title]');
const statusEl = this.root.querySelector('[data-return-status]');

if (titleEl) titleEl.textContent = `Return #${rma}`;
if (statusEl) {
// Map the raw status enum to the localized label injected by the
// template (same labels as the returns list); fall back to the raw value.
const statusLabels = {
OPEN: this.context.returnStatusOpen,
IN_PROGRESS: this.context.returnStatusInProgress,
CLOSED: this.context.returnStatusClosed,
};
statusEl.textContent = statusLabels[status] || status;
statusEl.classList.add(`returnDetails-statusBadge--${status.toLowerCase()}`);
}
}

renderShipping() {
const container = this.root.querySelector('[data-return-shipping]');
if (!container) return;

const { address, method, trackingNumber, dateShipped } = this.pageData.shipping;
const addressLine = [
address.fullName,
address.address1,
`${address.city}, ${address.state} ${address.zip}`,
address.country,
].filter(Boolean).join(', ');
const methodLine = `${method} (${trackingNumber}). Shipped on ${dateShipped}`;

const rows = [
{ label: 'Shipping address', value: addressLine },
{ label: 'Shipping method', value: methodLine },
];

container.innerHTML = rows.map(({ label, value }) => `
<div class="returnDetails-shippingRow">
<dt class="returnDetails-shippingLabel">${escape(label)}</dt>
<dd class="returnDetails-shippingValue">${escape(value)}</dd>
</div>`).join('');
}

renderItems() {
const list = this.root.querySelector('[data-return-items]');
if (!list) return;

const { items } = this.pageData;

list.innerHTML = items.map(item => this.itemTemplate(item)).join('');
}

itemTemplate(item) {
const resolutionLabel = item.requestedResolution ? item.requestedResolution.label : '';

return `
<li class="returnDetails-item" data-item-id="${escape(item.id)}">
<div class="returnDetails-itemThumbnailWrapper">
<img class="returnDetails-itemThumbnail"
src="${escape(item.thumbnailUrl)}"
alt="${escape(item.name)}">
</div>
<div class="returnDetails-itemInfo">
<p class="returnDetails-itemName">${escape(item.name)}</p>
${item.variant ? `<p class="returnDetails-itemVariant">${escape(item.variant)}</p>` : ''}
<p class="returnDetails-itemPrice">${escape(item.formatted_price)}</p>
</div>
<dl class="returnDetails-itemMeta">
<div class="returnDetails-itemMetaRow">
<dt class="returnDetails-itemMetaLabel">Return Quantity:</dt>
<dd class="returnDetails-itemMetaValue">${escape(item.quantity)}</dd>
</div>
<div class="returnDetails-itemMetaRow">
<dt class="returnDetails-itemMetaLabel">Request</dt>
<dd class="returnDetails-itemMetaValue">${escape(resolutionLabel)}</dd>
</div>
<div class="returnDetails-itemMetaRow">
<dt class="returnDetails-itemMetaLabel">Reason</dt>
<dd class="returnDetails-itemMetaValue">${escape(item.requestedReason)}</dd>
</div>
</dl>
</li>`;
}

renderSummary() {
const summary = this.root.querySelector('[data-return-summary]');
if (!summary) return;

const { opened, lastUpdated, order } = this.pageData;
const rows = [
{ label: 'Submitted:', value: opened },
{ label: 'Last update:', value: lastUpdated },
{ label: 'Order#:', value: order.id },
];

summary.innerHTML = rows.map(({ label, value }) => `
<div class="returnDetails-summaryRow">
<dt class="returnDetails-summaryLabel">${escape(label)}</dt>
<dd class="returnDetails-summaryValue">${escape(value)}</dd>
</div>`).join('');
}
}
3 changes: 3 additions & 0 deletions assets/scss/components/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@
// Add Return
@import "stencil/addReturn/component";

// Return Details
@import "stencil/returnDetails/component";

// Product Carousel
@import "stencil/productCarousel/component";

Expand Down
41 changes: 41 additions & 0 deletions assets/scss/components/stencil/account/_account.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,32 @@
}
}

.account-listItem-summary {
align-items: center;
display: flex;
flex-wrap: wrap;
gap: spacing("single");
justify-content: space-between;
margin-bottom: spacing("single");
}

.account-listItem-summary-main {
align-items: center;
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
gap: spacing("half");
min-width: 0;

.account-product-title {
margin: 0;
}

.account-orderStatus-label {
margin: 0;
}
}

.account-listShipping {
border-bottom: container("border");
font-weight: fontWeight("bold");
Expand Down Expand Up @@ -341,6 +367,21 @@
padding: spacing("eighth") spacing("third");
}

.account-orderStatus-label--open {
background-color: color("warning", "light");
color: stencilColor("color-textBase");
}

.account-orderStatus-label--inProgress {
background-color: color("success", "light");
color: stencilColor("color-textBase");
}

.account-orderStatus-label--closed {
background-color: color("greys", "lighter");
color: stencilColor("color-textBase");
}

.account-orderStatus-action {
color: stencilColor("color-textSecondary");
display: inline-block;
Expand Down
Loading