Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a45ab43
Release: Catalyst CLI 1.0.0 and create-catalyst 2.0.0 (merge alpha → …
jorgemoya Jul 1, 2026
0043364
Version Packages (`canary`) (#3078)
github-actions[bot] Jul 2, 2026
61413d3
fix(cli) - Add repository field to @bigcommerce/catalyst package.json…
jorgemoya Jul 2, 2026
c6b3b07
fix(core): support required custom customer fields in Account Setting…
chanceaclark Jul 6, 2026
67a2711
ci: TRAC-881 Hook up Linear releases via linear-release-action (#3085)
chanceaclark Jul 7, 2026
a763acc
feat(promotions): PROMO-1507 add featured promotions callout to pdp a…
bc-vivekaggarwal Jul 7, 2026
6184ef2
fix(ci): TRAC-881 Scope Linear release sync to core/** changes (#3086)
chanceaclark Jul 7, 2026
cb7d025
fix: TRAC-296: Respect blog visibility setting in footer nav (#3087)
jairo-bc Jul 8, 2026
aadaf27
feat(cli): add domains claim command for cross-store domain ownership…
jorgemoya Jul 8, 2026
ce03afb
fix(cli) - Don't frame clear user-actionable errors as bugs to report…
jorgemoya Jul 8, 2026
4ef6b2c
ci: TRAC-881 Drop the base_ref bootstrap for Linear sync (#3090)
chanceaclark Jul 8, 2026
2b7f2cc
feat(product): display product videos on the PDP (#3083)
jorgemoya Jul 8, 2026
bfeef73
feat(cli): add channel create command (#3093)
jorgemoya Jul 10, 2026
8d9f9a9
feat(cli): add domains transfer command for same-store project moves …
jorgemoya Jul 10, 2026
27373e8
fix(cli): validate channel name and clarify invalid-name error (#3095)
jorgemoya Jul 13, 2026
05f600a
feat(cli): require --env-path instead of auto-loading .env.local (#3098)
jorgemoya Jul 13, 2026
f60aea6
Update translations (#3101)
bc-svc-local Jul 13, 2026
b4e5952
fix(cli): make auth login device-code UX consistent with create (#3099)
jorgemoya Jul 13, 2026
e3e5ab2
fix(cli): give a clear error on deploy when required env vars are mis…
jorgemoya Jul 13, 2026
c1d0f3d
fix(cli): show not-enabled guidance on 404 for project create (#3096)
jorgemoya Jul 13, 2026
fea7b30
fix(cli): add @bigcommerce/catalyst CLI as a devDependency on create …
jorgemoya Jul 13, 2026
bf58b13
fix(cli): add readable HTTP error messages for 4xx/5xx API failures (…
jorgemoya Jul 14, 2026
d09f8dc
fix: TRAC-284 Coalesce rapid cart quantity/delete clicks (#3103)
chanceaclark Jul 14, 2026
25d6471
LTRAC-955: feat - respect control panel default search sort settings …
parthshahp Jul 14, 2026
07cd41c
fix(cli): follow .env.example ordering and comments when writing .env…
jorgemoya Jul 14, 2026
8256b46
feat(core): TRAC-282 Update cart locale when switching language (#3109)
jordanarldt Jul 15, 2026
4975333
LTRAC-274: fix(core) - Disable webpage nav caching when logged in (#3…
jorgemoya Jul 15, 2026
552cfb5
LTRAC-1086: feat(cli) - Add --wrangler-version flag to build and depl…
jorgemoya Jul 16, 2026
0d42b12
fix(cli): prevent dependency install from hanging on link/deploy setu…
jorgemoya Jul 16, 2026
9565d76
feat(cli): rename BIGCOMMERCE_API_HOST → CATALYST_API_HOST and resolv…
jorgemoya Jul 16, 2026
5ca4c61
feat(cli): add `catalyst debug` command to gather diagnostics for bug…
jorgemoya Jul 16, 2026
42dfe9e
Version Packages (`canary`) (#3082)
github-actions[bot] Jul 16, 2026
dd6fcc4
Merge remote-tracking branch 'origin/canary' into sync-integrations-m…
jorgemoya Jul 16, 2026
c8a2e58
fix(core): await switchLocale in locale switcher after merge
jorgemoya Jul 16, 2026
f82938f
chore(makeswift): ignore @bigcommerce/create-catalyst in changesets c…
jorgemoya Jul 16, 2026
dbdc5a6
Version Packages (`integrations/makeswift`) (#3114)
github-actions[bot] Jul 16, 2026
62ac611
Merge tag '@bigcommerce/catalyst-makeswift@1.9.0' into sync-integrati…
chanceaclark Jul 30, 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 .changeset/sync-makeswift-1-9-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bigcommerce/catalyst-b2b-makeswift": minor
---

Pulls in changes from the `@bigcommerce/catalyst-makeswift@1.9.0` release. For more information, see the [changelog entry](https://github.com/bigcommerce/catalyst/blob/da09e6304d1467ac6193f4d5eb07107a8933e0dc/core/CHANGELOG.md#190).
3 changes: 3 additions & 0 deletions core/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@ build-config.json
# OpenNext
.open-next
.wrangler

# Catalyst CLI config
.bigcommerce
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const CategoryPageQuery = graphql(
entityId
name
path
defaultProductSort
...BreadcrumbsFragment
seo {
pageTitle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ export default async function Category(props: Props) {

const taxDisplay = settings?.tax?.plp;

const categoryDefaultSort =
category.defaultProductSort && category.defaultProductSort !== 'DEFAULT'
? category.defaultProductSort.toLowerCase()
: 'featured';

const streamableFacetedSearch = Streamable.from(async () => {
const searchParams = await props.searchParams;
const currencyCode = await getPreferredCurrencyCode();
Expand All @@ -142,12 +147,14 @@ export default async function Category(props: Props) {
customerAccessToken,
);
const parsedSearchParams = loadSearchParams?.(searchParams) ?? {};
const sort = typeof searchParams.sort === 'string' ? searchParams.sort : categoryDefaultSort;

const search = await fetchFacetedSearch(
{
...searchParams,
...parsedSearchParams,
category: categoryId,
sort,
},
currencyCode,
customerAccessToken,
Expand Down Expand Up @@ -279,7 +286,7 @@ export default async function Category(props: Props) {
resetFiltersLabel={t('FacetedSearch.resetFilters')}
showCompare={productComparisonsEnabled}
showRating={showRating}
sortDefaultValue="featured"
sortDefaultValue={categoryDefaultSort}
sortLabel={t('SortBy.sortBy')}
sortOptions={[
{ value: 'featured', label: t('SortBy.featuredItems') },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,11 @@ const getProductSearchResults = cache(

const response = await client.fetch({
document: GetProductSearchResultsQuery,
variables: { ...filterArgs, ...paginationArgs, currencyCode },
variables: {
...filterArgs,
...paginationArgs,
currencyCode,
},
customerAccessToken,
fetchOptions: customerAccessToken ? { cache: 'no-store' } : { next: { revalidate: 300 } },
});
Expand Down
3 changes: 3 additions & 0 deletions core/app/[locale]/(default)/(faceted)/search/page-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const SearchPageQuery = graphql(`
tax {
plp
}
search {
defaultSearchProductSort
}
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion core/app/[locale]/(default)/(faceted)/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ export default async function Search(props: Props) {

const taxDisplay = settings?.tax?.plp;

const defaultProductSort = settings?.search.defaultSearchProductSort;

const streamableFacetedSearch = Streamable.from(async () => {
const searchParams = await props.searchParams;
const customerAccessToken = await getSessionCustomerAccessToken();
Expand All @@ -98,11 +100,13 @@ export default async function Search(props: Props) {
customerAccessToken,
);
const parsedSearchParams = loadSearchParams?.(searchParams) ?? {};
const sort = typeof searchParams.sort === 'string' ? searchParams.sort : defaultProductSort;

const search = await fetchFacetedSearch(
{
...searchParams,
...parsedSearchParams,
sort,
},
currencyCode,
customerAccessToken,
Expand Down Expand Up @@ -261,7 +265,7 @@ export default async function Search(props: Props) {
resetFiltersLabel={t('FacetedSearch.resetFilters')}
showCompare={productComparisonsEnabled}
showRating={showRating}
sortDefaultValue="featured"
sortDefaultValue={defaultProductSort?.toLowerCase() ?? 'featured'}
sortLabel={t('SortBy.sortBy')}
sortOptions={[
{ value: 'featured', label: t('SortBy.featuredItems') },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { parseWithZod } from '@conform-to/zod';
import { revalidateTag } from 'next/cache';
import { getTranslations } from 'next-intl/server';

import { Field, FieldGroup } from '@/vibes/soul/form/dynamic-form/schema';
import { updateAccountSchema } from '@/vibes/soul/sections/account-settings/schema';
import { UpdateAccountAction } from '@/vibes/soul/sections/account-settings/update-account-form';
import { State } from '@/vibes/soul/sections/account-settings/update-account-form';
import { getSessionCustomerAccessToken } from '~/auth';
import { client } from '~/client';
import { graphql } from '~/client/graphql';
import { graphql, VariablesOf } from '~/client/graphql';
import { TAGS } from '~/client/tags';

const UpdateCustomerMutation = graphql(`
Expand All @@ -19,6 +20,8 @@ const UpdateCustomerMutation = graphql(`
customer {
firstName
lastName
email
company
}
errors {
__typename
Expand All @@ -43,11 +46,81 @@ const UpdateCustomerMutation = graphql(`
}
`);

export const updateCustomer: UpdateAccountAction = async (prevState, formData) => {
type FormFieldsInput = NonNullable<
VariablesOf<typeof UpdateCustomerMutation>['input']['formFields']
>;

/*
* BigCommerce re-validates every required custom customer field on every updateCustomer call,
* even ones already satisfied by a prior save (see issue #3074) - so any required custom field
* rendered in the form must always be resent here, not just the ones the user actually changed.
*/
function buildFormFieldsInput(
fields: Array<Field | FieldGroup<Field>>,
value: Record<string, unknown>,
): FormFieldsInput {
const flatFields = fields.flatMap((field) => (Array.isArray(field) ? field : [field]));

return {
checkboxes: flatFields
.filter((field) => field.type === 'checkbox-group')
.filter((field) => Boolean(value[field.name]))
.map((field) => {
const rawValue = value[field.name];
const rawValues = Array.isArray(rawValue) ? rawValue : [rawValue];

return {
fieldEntityId: Number(field.id),
fieldValueEntityIds: rawValues.map(Number),
};
}),
multipleChoices: flatFields
.filter((field) => ['radio-group', 'button-radio-group', 'select'].includes(field.type))
.filter((field) => Boolean(value[field.name]))
.map((field) => ({
fieldEntityId: Number(field.id),
fieldValueEntityId: Number(value[field.name]),
})),
numbers: flatFields
.filter((field) => field.type === 'number')
.filter((field) => Boolean(value[field.name]))
.map((field) => ({
fieldEntityId: Number(field.id),
number: Number(value[field.name]),
})),
dates: flatFields
.filter((field) => field.type === 'date')
.filter((field) => Boolean(value[field.name]))
.map((field) => ({
fieldEntityId: Number(field.id),
date: new Date(String(value[field.name])).toISOString(),
})),
multilineTexts: flatFields
.filter((field) => field.type === 'textarea')
.filter((field) => Boolean(value[field.name]))
.map((field) => ({
fieldEntityId: Number(field.id),
multilineText: String(value[field.name]),
})),
texts: flatFields
.filter((field) => field.type === 'text' || field.type === 'email')
.filter((field) => Boolean(value[field.name]))
.map((field) => ({
fieldEntityId: Number(field.id),
text: String(value[field.name]),
})),
};
}

export async function updateCustomer(
{ fields }: { fields: Array<Field | FieldGroup<Field>> },
prevState: Awaited<State>,
formData: FormData,
): Promise<State> {
const t = await getTranslations('Account.Settings');
const customerAccessToken = await getSessionCustomerAccessToken();

const submission = parseWithZod(formData, { schema: updateAccountSchema });
const submission = parseWithZod(formData, { schema: updateAccountSchema(fields) });

if (submission.status !== 'success') {
return {
Expand All @@ -56,12 +129,20 @@ export const updateCustomer: UpdateAccountAction = async (prevState, formData) =
};
}

const { firstName, lastName, email, company, ...customFieldValues } = submission.value;

try {
const response = await client.fetch({
document: UpdateCustomerMutation,
customerAccessToken,
variables: {
input: submission.value,
input: {
firstName,
lastName,
email,
company,
formFields: buildFormFieldsInput(fields, customFieldValues),
},
},
fetchOptions: { cache: 'no-store' },
});
Expand Down Expand Up @@ -107,4 +188,4 @@ export const updateCustomer: UpdateAccountAction = async (prevState, formData) =
lastResult: submission.reply({ formErrors: [t('somethingWentWrong')] }),
};
}
};
}
11 changes: 9 additions & 2 deletions core/app/[locale]/(default)/account/settings/page-data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { getSessionCustomerAccessToken } from '~/auth';
import { client } from '~/client';
import { graphql, VariablesOf } from '~/client/graphql';
import { TAGS } from '~/client/tags';
import { FormFieldsFragment } from '~/data-transformers/form-field-transformer/fragment';
import {
FormFieldsFragment,
FormFieldValuesFragment,
} from '~/data-transformers/form-field-transformer/fragment';

const AccountSettingsQuery = graphql(
`
Expand All @@ -21,6 +24,9 @@ const AccountSettingsQuery = graphql(
lastName
company
isSubscribedToNewsletter
formFields {
...FormFieldValuesFragment
}
}
site {
settings {
Expand Down Expand Up @@ -50,7 +56,7 @@ const AccountSettingsQuery = graphql(
}
}
`,
[FormFieldsFragment],
[FormFieldsFragment, FormFieldValuesFragment],
);

type Variables = VariablesOf<typeof AccountSettingsQuery>;
Expand Down Expand Up @@ -96,6 +102,7 @@ export const getAccountSettingsQuery = cache(async ({ address, customer }: Props
return {
addressFields,
customerFields,
customerFormFieldValues: customerInfo.formFields,
customerInfo,
newsletterSettings,
passwordComplexitySettings,
Expand Down
54 changes: 53 additions & 1 deletion core/app/[locale]/(default)/account/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,49 @@ import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { getTranslations, setRequestLocale } from 'next-intl/server';

import { Field } from '@/vibes/soul/form/dynamic-form/schema';
import { AccountSettingsSection } from '@/vibes/soul/sections/account-settings';
import { formFieldTransformer } from '~/data-transformers/form-field-transformer';
import {
ACCOUNT_SETTINGS_FIELDS_TO_EXCLUDE,
mapFormFieldValueToName,
} from '~/data-transformers/form-field-transformer/utils';
import { exists } from '~/lib/utils';

import { changePassword } from './_actions/change-password';
import { updateCustomer } from './_actions/update-customer';
import { updateNewsletterSubscription } from './_actions/update-newsletter-subscription';
import { getAccountSettingsQuery } from './page-data';

function withDefaultValue(field: Field, value: unknown): Field {
if (field.type === 'checkbox-group') {
return { ...field, defaultValue: Array.isArray(value) ? value.map(String) : undefined };
}

if (typeof value !== 'string') {
return field;
}

switch (field.type) {
case 'radio-group':
case 'select':
case 'swatch-radio-group':
case 'card-radio-group':
case 'button-radio-group':
case 'checkbox':
case 'number':
case 'text':
case 'textarea':
case 'date':
case 'email':
case 'hidden':
return { ...field, defaultValue: value };

default:
return field;
}
}

interface Props {
params: Promise<{ locale: string }>;
}
Expand Down Expand Up @@ -47,6 +83,21 @@ export default async function Settings({ params }: Props) {
},
);

const customFieldValues = accountSettings.customerFormFieldValues.reduce<Record<string, unknown>>(
(acc, field) => ({ ...acc, ...mapFormFieldValueToName(field) }),
{},
);

const updateAccountCustomFields = accountSettings.customerFields
.filter((field) => !ACCOUNT_SETTINGS_FIELDS_TO_EXCLUDE.includes(field.entityId))
.map(formFieldTransformer)
.filter(exists)
.map((field) => withDefaultValue(field, customFieldValues[field.name]));

const updateCustomerWithFields = updateCustomer.bind(null, {
fields: updateAccountCustomFields,
});

return (
<AccountSettingsSection
account={accountSettings.customerInfo}
Expand All @@ -63,7 +114,8 @@ export default async function Settings({ params }: Props) {
newsletterSubscriptionTitle={t('NewsletterSubscription.title')}
passwordComplexitySettings={accountSettings.passwordComplexitySettings}
title={t('title')}
updateAccountAction={updateCustomer}
updateAccountAction={updateCustomerWithFields}
updateAccountCustomFields={updateAccountCustomFields}
updateAccountSubmitLabel={t('cta')}
updateNewsletterSubscriptionAction={updateNewsletterSubscriptionActionWithCustomerInfo}
/>
Expand Down
Loading
Loading