From b9c911927d9d3db95544ca8354e731ad6f11e5be Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Mon, 31 Aug 2026 18:57:44 -0300 Subject: [PATCH 1/4] chore: use beta uicore --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 5245ac019..294f1f965 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "moment-duration-format": "^2.3.2", "moment-timezone": "^0.5.33", "mui-color-input": "^9.0.0", - "openstack-uicore-foundation": "5.0.56", + "openstack-uicore-foundation": "5.0.59-beta.0", "p-limit": "^6.1.0", "path-browserify": "^1.0.1", "postcss-loader": "^6.2.1", diff --git a/yarn.lock b/yarn.lock index 49830f251..8edb8004c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9042,10 +9042,10 @@ open@^10.0.3: is-inside-container "^1.0.0" wsl-utils "^0.1.0" -openstack-uicore-foundation@5.0.56: - version "5.0.56" - resolved "https://registry.npmjs.org/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.56.tgz#806dc75481fb136295cf3f33a2d6da6536fa62cd" - integrity sha512-51p1q9Emeiurni5ttDeYFmPa9q0Su1bg49HgyqZLyfBqB1JbaaNwSmyChc590WBRhKYKEdKKqF8lYoVORCWYtA== +openstack-uicore-foundation@5.0.59-beta.0: + version "5.0.59-beta.0" + resolved "https://registry.yarnpkg.com/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.59-beta.0.tgz#86576e53df035265bcf89ee7621304d77ae099b4" + integrity sha512-kL2aGDHTli6wmgfG6op2hhGzVGSTgbOF5S6cwmqOKWKg4bH9cmjXFN9yiJOFigrUKtBgaF0gE6wb4439DBf4sw== dependencies: use-sync-external-store "^1.6.0" From d7aff91cd2cff78e478f0a90b99b98d90fee69a7 Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Wed, 2 Sep 2026 10:49:52 -0300 Subject: [PATCH 2/4] chore: new uicore version --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 294f1f965..15d27a8fa 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "moment-duration-format": "^2.3.2", "moment-timezone": "^0.5.33", "mui-color-input": "^9.0.0", - "openstack-uicore-foundation": "5.0.59-beta.0", + "openstack-uicore-foundation": "5.0.59-beta.1", "p-limit": "^6.1.0", "path-browserify": "^1.0.1", "postcss-loader": "^6.2.1", diff --git a/yarn.lock b/yarn.lock index 8edb8004c..93a5ecb24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9042,10 +9042,10 @@ open@^10.0.3: is-inside-container "^1.0.0" wsl-utils "^0.1.0" -openstack-uicore-foundation@5.0.59-beta.0: - version "5.0.59-beta.0" - resolved "https://registry.yarnpkg.com/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.59-beta.0.tgz#86576e53df035265bcf89ee7621304d77ae099b4" - integrity sha512-kL2aGDHTli6wmgfG6op2hhGzVGSTgbOF5S6cwmqOKWKg4bH9cmjXFN9yiJOFigrUKtBgaF0gE6wb4439DBf4sw== +openstack-uicore-foundation@5.0.59-beta.1: + version "5.0.59-beta.1" + resolved "https://registry.yarnpkg.com/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.59-beta.1.tgz#94f49ec975862a6b9b63e5756efa4013ad42f414" + integrity sha512-oBhvRGYxInGVOWLCbdTjJ0aCADkUaLZZurBiEQqHhCyE1XRThSBVvBn2VjOkY2vf2uY/APYtLABxTVlKdobQPA== dependencies: use-sync-external-store "^1.6.0" From 4d65e96a2b039c4c242382b40cc86c137965dc2e Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Wed, 2 Sep 2026 16:04:18 -0300 Subject: [PATCH 3/4] chore: update uicore --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 15d27a8fa..42e12ecd1 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "moment-duration-format": "^2.3.2", "moment-timezone": "^0.5.33", "mui-color-input": "^9.0.0", - "openstack-uicore-foundation": "5.0.59-beta.1", + "openstack-uicore-foundation": "5.0.59-beta.2", "p-limit": "^6.1.0", "path-browserify": "^1.0.1", "postcss-loader": "^6.2.1", diff --git a/yarn.lock b/yarn.lock index 93a5ecb24..37b985b28 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9042,10 +9042,10 @@ open@^10.0.3: is-inside-container "^1.0.0" wsl-utils "^0.1.0" -openstack-uicore-foundation@5.0.59-beta.1: - version "5.0.59-beta.1" - resolved "https://registry.yarnpkg.com/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.59-beta.1.tgz#94f49ec975862a6b9b63e5756efa4013ad42f414" - integrity sha512-oBhvRGYxInGVOWLCbdTjJ0aCADkUaLZZurBiEQqHhCyE1XRThSBVvBn2VjOkY2vf2uY/APYtLABxTVlKdobQPA== +openstack-uicore-foundation@5.0.59-beta.2: + version "5.0.59-beta.2" + resolved "https://registry.yarnpkg.com/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.59-beta.2.tgz#f52899f8c610a6e03e1b8c2c49d2d179438c2de8" + integrity sha512-+h8yDoMHpkrqRL1FJ8+0o7itX0kLSjaOoe7uqvmyOcHYA3W/TCLFppBgi2WeSXNXbQlKpPcbuEJGy/6s8TkcPA== dependencies: use-sync-external-store "^1.6.0" From ebd9ad838cdeecf290c1e4bb0cbfba48a110c693 Mon Sep 17 00:00:00 2001 From: Santiago Palenque Date: Mon, 7 Sep 2026 18:16:50 -0300 Subject: [PATCH 4/4] chore: apply pr review bugfixes --- package.json | 2 +- .../__tests__/edit-cart-form.test.js | 68 +++++++++++++++++++ .../components/edit-form/index.js | 18 ++--- .../components/edit-form/quantity-schema.js | 16 +++++ yarn.lock | 8 +-- 5 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/quantity-schema.js diff --git a/package.json b/package.json index 42e12ecd1..abe08973c 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "moment-duration-format": "^2.3.2", "moment-timezone": "^0.5.33", "mui-color-input": "^9.0.0", - "openstack-uicore-foundation": "5.0.59-beta.2", + "openstack-uicore-foundation": "5.0.59-beta.4", "p-limit": "^6.1.0", "path-browserify": "^1.0.1", "postcss-loader": "^6.2.1", diff --git a/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/__tests__/edit-cart-form.test.js b/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/__tests__/edit-cart-form.test.js index 6482f2aee..2c421ee83 100644 --- a/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/__tests__/edit-cart-form.test.js +++ b/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/__tests__/edit-cart-form.test.js @@ -98,6 +98,7 @@ import configureMockStore from "redux-mock-store"; import thunk from "redux-thunk"; import showConfirmDialog from "../../../../../../../../components/mui/showConfirmDialog"; import EditCartForm from "../edit-cart-form"; +import { buildGlobalQuantitySchema } from "../quantity-schema"; /* eslint-enable import/first */ const middlewares = [thunk]; @@ -577,4 +578,71 @@ describe("EditCartForm", () => { }); }); }); + + describe("EditForm - buildInitialValues (out-of-stock default_quantity)", () => { + const buildQuantityInitialValue = (item) => { + const hasStock = + !item.is_sold_out && item.remaining_quantity_sponsor !== 0; + return hasStock + ? item.quantity || item.default_quantity || 0 + : item.quantity || 0; + }; + + it("ignores default_quantity when the item is sold out for the show", () => { + const item = { is_sold_out: true, default_quantity: 1 }; + expect(buildQuantityInitialValue(item)).toBe(0); + }); + + it("ignores default_quantity when the sponsor's remaining quantity is 0", () => { + const item = { remaining_quantity_sponsor: 0, default_quantity: 1 }; + expect(buildQuantityInitialValue(item)).toBe(0); + }); + + it("keeps the sponsor's own existing quantity even when out of stock", () => { + const item = { is_sold_out: true, quantity: 3, default_quantity: 1 }; + expect(buildQuantityInitialValue(item)).toBe(3); + }); + + it("still applies default_quantity when the item has stock", () => { + const item = { is_sold_out: false, default_quantity: 1 }; + expect(buildQuantityInitialValue(item)).toBe(1); + }); + }); + + describe("EditForm - buildValidationSchema (quantity cap)", () => { + it("rejects a quantity above remaining_quantity_show when it is the tighter axis", async () => { + const schema = buildGlobalQuantitySchema({ + remaining_quantity_show: 2, + remaining_quantity_sponsor: 5 + }); + await expect(schema.isValid(3)).resolves.toBe(false); + await expect(schema.isValid(2)).resolves.toBe(true); + }); + + it("rejects a quantity above remaining_quantity_sponsor when it is the tighter axis", async () => { + const schema = buildGlobalQuantitySchema({ + remaining_quantity_show: 8, + remaining_quantity_sponsor: 3 + }); + await expect(schema.isValid(4)).resolves.toBe(false); + await expect(schema.isValid(3)).resolves.toBe(true); + }); + + it("applies no upper bound when both remaining quantities are null", async () => { + const schema = buildGlobalQuantitySchema({ + remaining_quantity_show: null, + remaining_quantity_sponsor: null + }); + await expect(schema.isValid(1000)).resolves.toBe(true); + }); + + it("rejects any positive quantity when remaining_quantity_show is 0 (boundary)", async () => { + const schema = buildGlobalQuantitySchema({ + remaining_quantity_show: 0, + remaining_quantity_sponsor: 5 + }); + await expect(schema.isValid(1)).resolves.toBe(false); + await expect(schema.isValid(0)).resolves.toBe(true); + }); + }); }); diff --git a/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/index.js b/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/index.js index 092d71263..53da95a94 100644 --- a/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/index.js +++ b/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/index.js @@ -25,6 +25,7 @@ import MuiFormItemTable, { } from "openstack-uicore-foundation/lib/components/mui/form-item-table"; import { DISCOUNT_TYPES } from "../../../../../../../utils/constants"; import showConfirmDialog from "../../../../../../../components/mui/showConfirmDialog"; +import { buildGlobalQuantitySchema } from "./quantity-schema"; const parseValue = (item, timeZone) => { switch (item.type) { @@ -146,8 +147,10 @@ const buildInitialValues = (form, timeZone) => { // add notes acc[`i-${item.form_item_id}-c-global-f-notes`] = item.notes || ""; // if no quantity inputs we add the global quantity input - acc[`i-${item.form_item_id}-c-global-f-quantity`] = - item.quantity || item.default_quantity || 0; + const hasStock = !item.is_sold_out && item.remaining_quantity_sponsor !== 0; + acc[`i-${item.form_item_id}-c-global-f-quantity`] = hasStock + ? item.quantity || item.default_quantity || 0 + : item.quantity || 0; // custom rate acc[`i-${item.form_item_id}-c-global-f-custom_rate`] = item.custom_rate || item.rates.custom || 0; @@ -168,16 +171,7 @@ const buildValidationSchema = (items) => { // notes acc[`i-${item.form_item_id}-c-global-f-notes`] = yup.string(); // validation for the global quantity input - let globalQtySchema = yup.number().min(0, " "); - - if (item.quantity_limit_per_sponsor > 0) { - globalQtySchema = globalQtySchema.max( - item.quantity_limit_per_sponsor, - " " - ); - } - globalQtySchema = globalQtySchema.required(" "); - acc[quantityKey] = globalQtySchema; + acc[quantityKey] = buildGlobalQuantitySchema(item); // custom rate acc[`i-${item.form_item_id}-c-global-f-custom_rate`] = yup.number(); diff --git a/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/quantity-schema.js b/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/quantity-schema.js new file mode 100644 index 000000000..7f544cfdb --- /dev/null +++ b/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/quantity-schema.js @@ -0,0 +1,16 @@ +import * as yup from "yup"; + +// The sponsor-facing quantity input is capped by whichever axis is tighter: +// what's left for the show, or what's left for this sponsor specifically. +// Either field being null means that axis has no cap. +export const buildGlobalQuantitySchema = (item) => { + let schema = yup.number().min(0, " "); + const maxQty = Math.min( + item.remaining_quantity_show ?? Infinity, + item.remaining_quantity_sponsor ?? Infinity + ); + if (Number.isFinite(maxQty)) { + schema = schema.max(maxQty, " "); + } + return schema.required(" "); +}; diff --git a/yarn.lock b/yarn.lock index 37b985b28..bd600d329 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9042,10 +9042,10 @@ open@^10.0.3: is-inside-container "^1.0.0" wsl-utils "^0.1.0" -openstack-uicore-foundation@5.0.59-beta.2: - version "5.0.59-beta.2" - resolved "https://registry.yarnpkg.com/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.59-beta.2.tgz#f52899f8c610a6e03e1b8c2c49d2d179438c2de8" - integrity sha512-+h8yDoMHpkrqRL1FJ8+0o7itX0kLSjaOoe7uqvmyOcHYA3W/TCLFppBgi2WeSXNXbQlKpPcbuEJGy/6s8TkcPA== +openstack-uicore-foundation@5.0.59-beta.4: + version "5.0.59-beta.4" + resolved "https://registry.yarnpkg.com/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.59-beta.4.tgz#084823ed10acc82b88c56ef532d97aa8a4b84ea2" + integrity sha512-kKpI3+9ctWOg3kp0Hgm8G+BrIfM2WDeuShOACOLqW7zCnme0IjJ837eawFOiwsttUrnokhNg7Pz7DN3uLM+t8A== dependencies: use-sync-external-store "^1.6.0"