{t("apply.alreadyMember")}{" "}
{t("signInWall.submit")}
diff --git a/src/app/[country]/[locale]/(wholesale)/wholesale/cart/WholesaleCartView.tsx b/src/app/[country]/[locale]/(wholesale)/wholesale/cart/WholesaleCartView.tsx
index 413ff11b..06f30128 100644
--- a/src/app/[country]/[locale]/(wholesale)/wholesale/cart/WholesaleCartView.tsx
+++ b/src/app/[country]/[locale]/(wholesale)/wholesale/cart/WholesaleCartView.tsx
@@ -19,7 +19,7 @@ import { WHOLESALE_MIN_QUANTITY } from "@/lib/wholesale";
* (checkout) flow, which resolves the wholesale surface from the cart id.
*/
export function WholesaleCartView() {
- const { cart, loading, updateItem, removeItem } = useCart();
+ const { cart, loading, updating, updateItem, removeItem } = useCart();
const pathname = usePathname();
// extractBasePath strips to /{country}/{locale}; the shared checkout lives there.
const storeBase = extractBasePath(pathname);
@@ -125,12 +125,14 @@ export function WholesaleCartView() {
onQuantityChange={(quantity) =>
updateItem(item.id, quantity)
}
+ disabled={updating}
/>
diff --git a/src/app/[country]/[locale]/(wholesale)/wholesale/sign-in/page.tsx b/src/app/[country]/[locale]/(wholesale)/wholesale/sign-in/page.tsx
new file mode 100644
index 00000000..ff068484
--- /dev/null
+++ b/src/app/[country]/[locale]/(wholesale)/wholesale/sign-in/page.tsx
@@ -0,0 +1,45 @@
+import { redirect } from "next/navigation";
+import { getCustomer } from "@/lib/data/customer";
+import { getWholesaleChannel } from "@/lib/data/wholesale";
+import { safeRedirectPath } from "@/lib/utils/path";
+import { WholesaleSignInWall } from "../_components/WholesaleSignInWall";
+
+interface WholesaleSignInPageProps {
+ params: Promise<{ country: string; locale: string }>;
+ // Repeated query keys arrive as an array, so accept what Next.js can deliver.
+ searchParams: Promise<{ redirect?: string | string[] }>;
+}
+
+/**
+ * Dedicated sign-in destination for the portal. On a `prices_hidden` channel
+ * the catalog root renders for guests, so "sign in" affordances can't point
+ * there — they'd land right back on the catalog (and, with `?redirect=`
+ * re-appended on every click, loop forever). This page always shows the
+ * sign-in wall (which also links to the apply form) and honours the same
+ * `?redirect=` contract; an already-authenticated buyer is bounced into the
+ * portal, where the gate resolves their approval state.
+ */
+export default async function WholesaleSignInPage({
+ params,
+ searchParams,
+}: WholesaleSignInPageProps) {
+ const { country, locale } = await params;
+ const { redirect: redirectParam } = await searchParams;
+ const basePath = `/${country}/${locale}`;
+
+ const [customer, channel] = await Promise.all([
+ getCustomer(),
+ getWholesaleChannel(),
+ ]);
+
+ if (customer) {
+ redirect(safeRedirectPath(redirectParam, `${basePath}/wholesale`));
+ }
+
+ return (
+