@vendure-platform/create 0.5.1 → 1.0.0-major.202609091053.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -36
- package/dist/add-to-existing.d.ts +19 -13
- package/dist/add-to-existing.js +1 -1
- package/dist/assets/demo-data/assets/branding/logo.svg +4 -0
- package/dist/assets/demo-data/assets/images/absorbent-pad.webp +0 -0
- package/dist/assets/demo-data/assets/images/adjustable-wrench.webp +0 -0
- package/dist/assets/demo-data/assets/images/anchor-bolt.webp +0 -0
- package/dist/assets/demo-data/assets/images/angle-grinder.webp +0 -0
- package/dist/assets/demo-data/assets/images/barrier-tape.webp +0 -0
- package/dist/assets/demo-data/assets/images/bench-grinder.webp +0 -0
- package/dist/assets/demo-data/assets/images/bracket.webp +0 -0
- package/dist/assets/demo-data/assets/images/broom.webp +0 -0
- package/dist/assets/demo-data/assets/images/bucket.webp +0 -0
- package/dist/assets/demo-data/assets/images/cable-gland.webp +0 -0
- package/dist/assets/demo-data/assets/images/cable-tie.webp +0 -0
- package/dist/assets/demo-data/assets/images/cable-tray.webp +0 -0
- package/dist/assets/demo-data/assets/images/cable.webp +0 -0
- package/dist/assets/demo-data/assets/images/chimney-base-kit.webp +0 -0
- package/dist/assets/demo-data/assets/images/chimney-cowl-bird-guard.webp +0 -0
- package/dist/assets/demo-data/assets/images/chimney-cowl-open.webp +0 -0
- package/dist/assets/demo-data/assets/images/chimney-storm-collar.webp +0 -0
- package/dist/assets/demo-data/assets/images/chimney-wall-bracket.webp +0 -0
- package/dist/assets/demo-data/assets/images/circular-saw.webp +0 -0
- package/dist/assets/demo-data/assets/images/cleaning-spray.webp +0 -0
- package/dist/assets/demo-data/assets/images/conduit.webp +0 -0
- package/dist/assets/demo-data/assets/images/cordless-drill.webp +0 -0
- package/dist/assets/demo-data/assets/images/drill-bits.webp +0 -0
- package/dist/assets/demo-data/assets/images/dust-mask.webp +0 -0
- package/dist/assets/demo-data/assets/images/ear-defenders.webp +0 -0
- package/dist/assets/demo-data/assets/images/ear-plugs.webp +0 -0
- package/dist/assets/demo-data/assets/images/extension-lead.webp +0 -0
- package/dist/assets/demo-data/assets/images/eye-wash-station.webp +0 -0
- package/dist/assets/demo-data/assets/images/face-shield.webp +0 -0
- package/dist/assets/demo-data/assets/images/file-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/first-aid-kit.webp +0 -0
- package/dist/assets/demo-data/assets/images/floor-sign.webp +0 -0
- package/dist/assets/demo-data/assets/images/flue-bend-45.webp +0 -0
- package/dist/assets/demo-data/assets/images/flue-bend-90.webp +0 -0
- package/dist/assets/demo-data/assets/images/flue-section-1000mm.webp +0 -0
- package/dist/assets/demo-data/assets/images/flue-section-250mm.webp +0 -0
- package/dist/assets/demo-data/assets/images/flue-section-500mm.webp +0 -0
- package/dist/assets/demo-data/assets/images/fuse.webp +0 -0
- package/dist/assets/demo-data/assets/images/glove-dispenser.webp +0 -0
- package/dist/assets/demo-data/assets/images/grinding-disc.webp +0 -0
- package/dist/assets/demo-data/assets/images/hacksaw.webp +0 -0
- package/dist/assets/demo-data/assets/images/hammer.webp +0 -0
- package/dist/assets/demo-data/assets/images/hand-sanitiser.webp +0 -0
- package/dist/assets/demo-data/assets/images/head-torch.webp +0 -0
- package/dist/assets/demo-data/assets/images/heat-gun.webp +0 -0
- package/dist/assets/demo-data/assets/images/hex-bolt.webp +0 -0
- package/dist/assets/demo-data/assets/images/hex-key-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/hex-nut.webp +0 -0
- package/dist/assets/demo-data/assets/images/hi-vis-jacket.webp +0 -0
- package/dist/assets/demo-data/assets/images/hi-vis-vest.webp +0 -0
- package/dist/assets/demo-data/assets/images/hose-clamp.webp +0 -0
- package/dist/assets/demo-data/assets/images/impact-driver.webp +0 -0
- package/dist/assets/demo-data/assets/images/jigsaw.webp +0 -0
- package/dist/assets/demo-data/assets/images/junction-box.webp +0 -0
- package/dist/assets/demo-data/assets/images/knee-pads.webp +0 -0
- package/dist/assets/demo-data/assets/images/machine-screw.webp +0 -0
- package/dist/assets/demo-data/assets/images/made-to-order-configurable-chimney.webp +0 -0
- package/dist/assets/demo-data/assets/images/mallet.webp +0 -0
- package/dist/assets/demo-data/assets/images/mcb.webp +0 -0
- package/dist/assets/demo-data/assets/images/mop.webp +0 -0
- package/dist/assets/demo-data/assets/images/paper-towels.webp +0 -0
- package/dist/assets/demo-data/assets/images/pipe-wrench.webp +0 -0
- package/dist/assets/demo-data/assets/images/pliers-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/pry-bar.webp +0 -0
- package/dist/assets/demo-data/assets/images/rcd.webp +0 -0
- package/dist/assets/demo-data/assets/images/reciprocating-saw.webp +0 -0
- package/dist/assets/demo-data/assets/images/rivet.webp +0 -0
- package/dist/assets/demo-data/assets/images/roof-flashing-high-pitch.webp +0 -0
- package/dist/assets/demo-data/assets/images/roof-flashing-low-pitch.webp +0 -0
- package/dist/assets/demo-data/assets/images/safety-boots.webp +0 -0
- package/dist/assets/demo-data/assets/images/safety-glasses.webp +0 -0
- package/dist/assets/demo-data/assets/images/safety-harness.webp +0 -0
- package/dist/assets/demo-data/assets/images/safety-helmet.webp +0 -0
- package/dist/assets/demo-data/assets/images/safety-sign.webp +0 -0
- package/dist/assets/demo-data/assets/images/sanding-disc.webp +0 -0
- package/dist/assets/demo-data/assets/images/saw-blade.webp +0 -0
- package/dist/assets/demo-data/assets/images/screwdriver-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/screwdriver.webp +0 -0
- package/dist/assets/demo-data/assets/images/self-tapping-screw.webp +0 -0
- package/dist/assets/demo-data/assets/images/socket-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/socket.webp +0 -0
- package/dist/assets/demo-data/assets/images/spanner-set.webp +0 -0
- package/dist/assets/demo-data/assets/images/spanner.webp +0 -0
- package/dist/assets/demo-data/assets/images/spill-kit.webp +0 -0
- package/dist/assets/demo-data/assets/images/spirit-level.webp +0 -0
- package/dist/assets/demo-data/assets/images/switch.webp +0 -0
- package/dist/assets/demo-data/assets/images/tape-measure.webp +0 -0
- package/dist/assets/demo-data/assets/images/terminal-block.webp +0 -0
- package/dist/assets/demo-data/assets/images/threaded-rod.webp +0 -0
- package/dist/assets/demo-data/assets/images/torque-wrench.webp +0 -0
- package/dist/assets/demo-data/assets/images/utility-knife.webp +0 -0
- package/dist/assets/demo-data/assets/images/wall-pass-sleeve.webp +0 -0
- package/dist/assets/demo-data/assets/images/wall-plug.webp +0 -0
- package/dist/assets/demo-data/assets/images/washer.webp +0 -0
- package/dist/assets/demo-data/assets/images/waste-bin.webp +0 -0
- package/dist/assets/demo-data/assets/images/wire-connector.webp +0 -0
- package/dist/assets/demo-data/assets/images/wire-cutters.webp +0 -0
- package/dist/assets/demo-data/assets/images/wood-screw.webp +0 -0
- package/dist/assets/demo-data/assets/images/work-gloves.webp +0 -0
- package/dist/assets/demo-data/assets/images/work-light.webp +0 -0
- package/dist/assets/demo-data/assets/initial-data.json +667 -0
- package/dist/assets/demo-data/assets/products.csv +779 -0
- package/dist/assets/storefront/next-env.d.ts +2 -1
- package/dist/assets/storefront/next.config.ts +14 -13
- package/dist/assets/storefront/package.json +3 -2
- package/dist/assets/storefront/src/app/[locale]/account/addresses/page.tsx +2 -0
- package/dist/assets/storefront/src/app/[locale]/account/approvals/page.tsx +2 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/business-units/page.tsx +2 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/page.tsx +2 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/page.tsx +2 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/roles/page.tsx +2 -0
- package/dist/assets/storefront/src/app/[locale]/account/company/users/page.tsx +2 -0
- package/dist/assets/storefront/src/app/[locale]/account/profile/page.tsx +2 -0
- package/dist/assets/storefront/src/app/[locale]/account/quick-order/page.tsx +2 -0
- package/dist/assets/storefront/src/app/[locale]/account/quotes/[id]/page.tsx +10 -6
- package/dist/assets/storefront/src/app/[locale]/cart/order-summary.tsx +8 -5
- package/dist/assets/storefront/src/app/[locale]/cart/promotion-code.tsx +9 -6
- package/dist/assets/storefront/src/app/[locale]/checkout/page.tsx +2 -0
- package/dist/assets/storefront/src/app/[locale]/layout.tsx +3 -3
- package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/order-confirmation.tsx +2 -60
- package/dist/assets/storefront/src/components/commerce/product-card-list.tsx +4 -2
- package/dist/assets/storefront/src/components/commerce/product-card.tsx +4 -2
- package/dist/assets/storefront/src/components/commerce/quote-action-buttons.tsx +31 -14
- package/dist/assets/storefront/src/config/capabilities.ts +41 -0
- package/dist/assets/storefront/src/config/scenario.ts +10 -0
- package/dist/assets/storefront/src/graphql-env.d.ts +47 -41
- package/dist/assets/storefront/src/i18n/server.ts +33 -5
- package/dist/assets/storefront/src/lib/backend-health.ts +11 -3
- package/dist/assets/storefront/src/lib/vendure/capability-documents.ts +246 -0
- package/dist/assets/storefront/src/lib/vendure/capability-schema.ts +358 -0
- package/dist/assets/storefront/src/lib/vendure/capability-ui.ts +15 -0
- package/dist/assets/storefront/src/lib/vendure/mutations.ts +835 -532
- package/dist/assets/storefront/src/lib/vendure/queries.ts +862 -519
- package/dist/assets/storefront/src/proxy.ts +11 -0
- package/dist/assets/storefront/src/schema.graphql +337 -243
- package/dist/assets/templates/README.md.hbs +25 -1
- package/dist/assets/templates/docker-compose.yml.hbs +4 -0
- package/dist/assets/templates/env.hbs +34 -9
- package/dist/assets/templates/environment.d.ts.hbs +1 -0
- package/dist/assets/templates/gitignore.hbs +7 -4
- package/dist/assets/templates/migrate.ts.hbs +55 -0
- package/dist/assets/templates/npmrc.hbs +7 -2
- package/dist/assets/templates/platform-edition.ts.hbs +21 -0
- package/dist/assets/templates/server-package.json.hbs +15 -8
- package/dist/assets/templates/vendure-config.ts.hbs +18 -0
- package/dist/bin/run.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/console-onboarding.d.ts +44 -0
- package/dist/console-onboarding.js +1 -0
- package/dist/hub-projects.d.ts +48 -0
- package/dist/hub-projects.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin-registry.d.ts +61 -2
- package/dist/plugin-registry.js +1 -1
- package/dist/registry.d.ts +16 -13
- package/dist/registry.js +1 -1
- package/dist/scaffold-shared.js +1 -1
- package/dist/scaffold-storefront-only.js +1 -1
- package/dist/scaffolder.d.ts +11 -0
- package/dist/scaffolder.js +1 -1
- package/dist/storefront-copier.d.ts +7 -2
- package/dist/storefront-copier.js +1 -1
- package/dist/template-renderer.d.ts +26 -5
- package/dist/template-renderer.js +1 -1
- package/dist/types.d.ts +54 -23
- package/package.json +14 -2
- package/dist/assets/storefront/src/middleware.ts +0 -11
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/
|
|
3
|
+
import "./.next/types/routes.d.ts";
|
|
4
|
+
import "./.next/types/root-params.d.ts";
|
|
4
5
|
|
|
5
6
|
// NOTE: This file should not be edited
|
|
6
7
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
|
@@ -9,19 +9,21 @@ const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
|
|
|
9
9
|
const nextConfig: NextConfig = {
|
|
10
10
|
// cacheComponents enables PPR + useCache + dynamicIO.
|
|
11
11
|
//
|
|
12
|
-
// Combining this with next-intl
|
|
13
|
-
// scopes can read the locale
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// Combining this with next-intl needs next/root-params so that 'use cache'
|
|
13
|
+
// scopes can read the locale (which becomes an automatic cache key) instead
|
|
14
|
+
// of headers(), which is forbidden in cache boundaries. next/root-params is
|
|
15
|
+
// stable as of Next.js 16.3 and needs no config flag; the 16.2-era
|
|
16
|
+
// `experimental.rootParams` opt-in no longer exists.
|
|
16
17
|
//
|
|
17
|
-
// Key constraints (as of Next.js 16.
|
|
18
|
-
// 1. No root layout above [locale]
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// 2. next/root-params
|
|
23
|
-
//
|
|
24
|
-
// as a
|
|
18
|
+
// Key constraints (as of Next.js 16.3 / next-intl 4.9):
|
|
19
|
+
// 1. No root layout above [locale]. Root params are the dynamic segments
|
|
20
|
+
// that sit above the root layout, so a layout.tsx at the app root
|
|
21
|
+
// would leave [locale] below it and next/root-params would export
|
|
22
|
+
// nothing.
|
|
23
|
+
// 2. next/root-params works in any Server Component, including shared
|
|
24
|
+
// utility modules, but not in Client Components, Server Actions or
|
|
25
|
+
// Route Handlers. Those must use getLocale() or take locale as a
|
|
26
|
+
// parameter — see src/i18n/server.ts.
|
|
25
27
|
// 3. Turbopack dev has a pre-existing compilation hang on this project
|
|
26
28
|
// (not i18n-related), so we use --webpack for dev and build.
|
|
27
29
|
//
|
|
@@ -30,7 +32,6 @@ const nextConfig: NextConfig = {
|
|
|
30
32
|
// https://aurorascharff.no/posts/implementing-nextjs-16-use-cache-with-next-intl-internationalization/
|
|
31
33
|
cacheComponents: true,
|
|
32
34
|
experimental: {
|
|
33
|
-
rootParams: true,
|
|
34
35
|
optimizePackageImports: ['lucide-react'],
|
|
35
36
|
},
|
|
36
37
|
env: {
|
|
@@ -37,16 +37,17 @@
|
|
|
37
37
|
"@radix-ui/react-toggle": "^1.1.10",
|
|
38
38
|
"@radix-ui/react-toggle-group": "^1.1.11",
|
|
39
39
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
40
|
-
"@vendure-platform/storefront-client": "^0.
|
|
40
|
+
"@vendure-platform/storefront-client": "^1.0.0-major.202609091053.0.0",
|
|
41
41
|
"class-variance-authority": "^0.7.1",
|
|
42
42
|
"clsx": "^2.1.1",
|
|
43
43
|
"cmdk": "^1.1.1",
|
|
44
44
|
"date-fns": "^4.1.0",
|
|
45
45
|
"embla-carousel-react": "^8.6.0",
|
|
46
46
|
"gql.tada": "^1.9.0",
|
|
47
|
+
"graphql": "^16.13.2",
|
|
47
48
|
"input-otp": "^1.4.2",
|
|
48
49
|
"lucide-react": "^0.544.0",
|
|
49
|
-
"next": "^16.
|
|
50
|
+
"next": "^16.3.3",
|
|
50
51
|
"next-intl": "^4.8.3",
|
|
51
52
|
"next-themes": "^0.4.6",
|
|
52
53
|
"react": " ^19.2.0",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ErrorState } from '@/components/shared/error-state';
|
|
2
|
+
import { pinRootLocale } from '@/i18n/server';
|
|
2
3
|
import { client } from '@/lib/vendure/api';
|
|
3
4
|
import { GetAvailableCountriesQuery, GetCustomerAddressesQuery } from '@/lib/vendure/queries';
|
|
4
5
|
import type { Metadata } from 'next';
|
|
@@ -11,6 +12,7 @@ export const metadata: Metadata = {
|
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
async function AddressesContent() {
|
|
15
|
+
await pinRootLocale();
|
|
14
16
|
const t = await getTranslations('Account');
|
|
15
17
|
const [addressesRes, countriesRes] = await Promise.all([
|
|
16
18
|
client.query(GetCustomerAddressesQuery, {}),
|
|
@@ -6,6 +6,7 @@ import { Button } from '@/components/ui/button';
|
|
|
6
6
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
7
7
|
import { ResultOf } from '@/graphql';
|
|
8
8
|
import { Link, redirect } from '@/i18n/navigation';
|
|
9
|
+
import { pinRootLocale } from '@/i18n/server';
|
|
9
10
|
import { formatDateTime } from '@/lib/format';
|
|
10
11
|
import { getActiveCustomer } from '@/lib/vendure/actions';
|
|
11
12
|
import { client } from '@/lib/vendure/api';
|
|
@@ -330,6 +331,7 @@ async function ApprovalsContent({ params }: { params: Promise<{ locale: string }
|
|
|
330
331
|
}
|
|
331
332
|
|
|
332
333
|
export default async function ApprovalsPage({ params }: { params: Promise<{ locale: string }> }) {
|
|
334
|
+
await pinRootLocale();
|
|
333
335
|
const t = await getTranslations('Approvals');
|
|
334
336
|
return (
|
|
335
337
|
<div>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ErrorState } from '@/components/shared/error-state';
|
|
2
2
|
import { readFragment } from '@/graphql';
|
|
3
3
|
import { redirect } from '@/i18n/navigation';
|
|
4
|
+
import { pinRootLocale } from '@/i18n/server';
|
|
4
5
|
import { getCompanyAccount } from '@/lib/vendure/actions';
|
|
5
6
|
import { client } from '@/lib/vendure/api';
|
|
6
7
|
import { CompanyBusinessUnitFragment } from '@/lib/vendure/fragments';
|
|
@@ -15,6 +16,7 @@ export const metadata: Metadata = {
|
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
async function BusinessUnitsContent() {
|
|
19
|
+
await pinRootLocale();
|
|
18
20
|
const [t, companyAccount] = await Promise.all([
|
|
19
21
|
getTranslations('Company.businessUnits'),
|
|
20
22
|
getCompanyAccount(),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CompanyStateBadge } from '@/components/commerce/company-state-badge';
|
|
2
2
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
3
3
|
import { redirect } from '@/i18n/navigation';
|
|
4
|
+
import { pinRootLocale } from '@/i18n/server';
|
|
4
5
|
import { getCompanyAccount, getMyCompanies } from '@/lib/vendure/actions';
|
|
5
6
|
import { Clock } from 'lucide-react';
|
|
6
7
|
import type { Metadata } from 'next';
|
|
@@ -13,6 +14,7 @@ export const metadata: Metadata = {
|
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
async function CompanyProfileContent() {
|
|
17
|
+
await pinRootLocale();
|
|
16
18
|
const [t, companyAccount] = await Promise.all([getTranslations('Company'), getCompanyAccount()]);
|
|
17
19
|
|
|
18
20
|
if (!companyAccount) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ErrorState } from '@/components/shared/error-state';
|
|
2
2
|
import { readFragment } from '@/graphql';
|
|
3
3
|
import { redirect } from '@/i18n/navigation';
|
|
4
|
+
import { pinRootLocale } from '@/i18n/server';
|
|
4
5
|
import { getCompanyAccount } from '@/lib/vendure/actions';
|
|
5
6
|
import { client } from '@/lib/vendure/api';
|
|
6
7
|
import { CompanyPaymentMethodFragment } from '@/lib/vendure/fragments';
|
|
@@ -15,6 +16,7 @@ export const metadata: Metadata = {
|
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
async function CompanyPaymentMethodsContent() {
|
|
19
|
+
await pinRootLocale();
|
|
18
20
|
const [t, companyAccount] = await Promise.all([
|
|
19
21
|
getTranslations('Company.paymentMethods'),
|
|
20
22
|
getCompanyAccount(),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFragment } from '@/graphql';
|
|
2
2
|
import { redirect } from '@/i18n/navigation';
|
|
3
|
+
import { pinRootLocale } from '@/i18n/server';
|
|
3
4
|
import { getCompanyAccount } from '@/lib/vendure/actions';
|
|
4
5
|
import { client } from '@/lib/vendure/api';
|
|
5
6
|
import { CompanyPermissionFragment, CompanyRoleFragment } from '@/lib/vendure/fragments';
|
|
@@ -14,6 +15,7 @@ export const metadata: Metadata = {
|
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
async function CompanyRolesContent() {
|
|
18
|
+
await pinRootLocale();
|
|
17
19
|
const [t, companyAccount] = await Promise.all([getTranslations('Company.roles'), getCompanyAccount()]);
|
|
18
20
|
|
|
19
21
|
if (!companyAccount) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFragment } from '@/graphql';
|
|
2
2
|
import { redirect } from '@/i18n/navigation';
|
|
3
|
+
import { pinRootLocale } from '@/i18n/server';
|
|
3
4
|
import { getCompanyAccount } from '@/lib/vendure/actions';
|
|
4
5
|
import { client } from '@/lib/vendure/api';
|
|
5
6
|
import {
|
|
@@ -23,6 +24,7 @@ export const metadata: Metadata = {
|
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
async function CompanyUsersContent() {
|
|
27
|
+
await pinRootLocale();
|
|
26
28
|
const [t, companyAccount] = await Promise.all([getTranslations('Company.users'), getCompanyAccount()]);
|
|
27
29
|
|
|
28
30
|
if (!companyAccount) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { pinRootLocale } from '@/i18n/server';
|
|
1
2
|
import { getActiveCustomer } from '@/lib/vendure/actions';
|
|
2
3
|
import type { Metadata } from 'next';
|
|
3
4
|
import { getTranslations } from 'next-intl/server';
|
|
@@ -11,6 +12,7 @@ export const metadata: Metadata = {
|
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
async function ProfileContent() {
|
|
15
|
+
await pinRootLocale();
|
|
14
16
|
const [customer, t] = await Promise.all([getActiveCustomer(), getTranslations('Account')]);
|
|
15
17
|
|
|
16
18
|
return (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { redirect } from '@/i18n/navigation';
|
|
2
|
+
import { pinRootLocale } from '@/i18n/server';
|
|
2
3
|
import { getActiveCustomer } from '@/lib/vendure/actions';
|
|
3
4
|
import type { Metadata } from 'next';
|
|
4
5
|
import { getTranslations } from 'next-intl/server';
|
|
@@ -9,6 +10,7 @@ export const metadata: Metadata = {
|
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
export default async function QuickOrderPage() {
|
|
13
|
+
await pinRootLocale();
|
|
12
14
|
const [customer, t] = await Promise.all([getActiveCustomer(), getTranslations('QuickOrder')]);
|
|
13
15
|
if (!customer) {
|
|
14
16
|
return await redirect('/sign-in');
|
|
@@ -8,12 +8,14 @@ import { QuoteVersionComparison } from '@/components/commerce/quote-version-comp
|
|
|
8
8
|
import { Button } from '@/components/ui/button';
|
|
9
9
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
10
10
|
import { Separator } from '@/components/ui/separator';
|
|
11
|
+
import { hasCapability } from '@/config/capabilities';
|
|
11
12
|
import { Link, redirect } from '@/i18n/navigation';
|
|
12
13
|
import { getActiveChannelToken } from '@/lib/channel-server';
|
|
13
14
|
import { formatDate } from '@/lib/format';
|
|
14
15
|
import { client } from '@/lib/vendure/api';
|
|
15
16
|
import { getActiveChannelCached } from '@/lib/vendure/cached';
|
|
16
|
-
import {
|
|
17
|
+
import { getQuoteNegotiationData } from '@/lib/vendure/capability-ui';
|
|
18
|
+
import { GetMyQuoteDetailQuery, type QuoteComment, type QuoteVersion } from '@/lib/vendure/queries';
|
|
17
19
|
import { AlertTriangle, ChevronLeft, Info } from 'lucide-react';
|
|
18
20
|
import type { Metadata } from 'next';
|
|
19
21
|
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
|
@@ -105,10 +107,11 @@ async function QuoteDetailContent({
|
|
|
105
107
|
return await redirect('/account/quotes');
|
|
106
108
|
}
|
|
107
109
|
const version = quote.currentVersion;
|
|
110
|
+
const negotiation = getQuoteNegotiationData<QuoteComment, QuoteVersion>(quote);
|
|
108
111
|
const isTerminal = TERMINAL_STATUSES.has(quote.status);
|
|
109
|
-
const v1 =
|
|
112
|
+
const v1 = negotiation.versions.find((item) => item.versionNumber === 1);
|
|
110
113
|
const v1LineItems =
|
|
111
|
-
v1 &&
|
|
114
|
+
v1 && negotiation.versions.length > 1
|
|
112
115
|
? v1.lineItems.map((item) => ({ sku: item.sku, unitPrice: item.unitPrice }))
|
|
113
116
|
: undefined;
|
|
114
117
|
|
|
@@ -167,7 +170,7 @@ async function QuoteDetailContent({
|
|
|
167
170
|
{version && (
|
|
168
171
|
<QuoteVersionComparison
|
|
169
172
|
currentVersion={version}
|
|
170
|
-
versions={
|
|
173
|
+
versions={negotiation.versions}
|
|
171
174
|
currencyCode={currencyCode}
|
|
172
175
|
/>
|
|
173
176
|
)}
|
|
@@ -223,8 +226,8 @@ async function QuoteDetailContent({
|
|
|
223
226
|
|
|
224
227
|
{/* Activity Timeline */}
|
|
225
228
|
<QuoteActivityTimeline
|
|
226
|
-
versions={
|
|
227
|
-
comments={
|
|
229
|
+
versions={negotiation.versions}
|
|
230
|
+
comments={negotiation.comments}
|
|
228
231
|
currentVersionNumber={version?.versionNumber}
|
|
229
232
|
currencyCode={currencyCode}
|
|
230
233
|
/>
|
|
@@ -276,6 +279,7 @@ async function QuoteDetailContent({
|
|
|
276
279
|
quoteId={quote.id}
|
|
277
280
|
status={quote.status}
|
|
278
281
|
convertedOrderId={quote.convertedOrderId}
|
|
282
|
+
hasQuoteNegotiation={hasCapability('quote.negotiation')}
|
|
279
283
|
/>
|
|
280
284
|
</CardContent>
|
|
281
285
|
</Card>
|
|
@@ -18,8 +18,8 @@ type ActiveOrder = {
|
|
|
18
18
|
shippingWithTax: number;
|
|
19
19
|
total: number;
|
|
20
20
|
totalWithTax: number;
|
|
21
|
-
storeCreditsApplied
|
|
22
|
-
totalWithTaxAfterStoreCredit
|
|
21
|
+
storeCreditsApplied?: Array<{ code: string; balance: number; amountDeducted: number }>;
|
|
22
|
+
totalWithTaxAfterStoreCredit?: number;
|
|
23
23
|
lines: Array<{
|
|
24
24
|
id: string;
|
|
25
25
|
quantity: number;
|
|
@@ -50,7 +50,8 @@ type ActiveOrder = {
|
|
|
50
50
|
|
|
51
51
|
export async function OrderSummary({ activeOrder }: { activeOrder: ActiveOrder }) {
|
|
52
52
|
const t = await getTranslations('Cart');
|
|
53
|
-
const
|
|
53
|
+
const storeCreditsApplied = activeOrder.storeCreditsApplied ?? [];
|
|
54
|
+
const hasCreditsApplied = storeCreditsApplied.length > 0;
|
|
54
55
|
const authToken = await getAuthToken();
|
|
55
56
|
const taxTotal = activeOrder.totalWithTax - activeOrder.total;
|
|
56
57
|
const MIN_QTY_FOR_QUOTE = 50;
|
|
@@ -133,7 +134,7 @@ export async function OrderSummary({ activeOrder }: { activeOrder: ActiveOrder }
|
|
|
133
134
|
</div>
|
|
134
135
|
)}
|
|
135
136
|
{hasCreditsApplied &&
|
|
136
|
-
|
|
137
|
+
storeCreditsApplied.map((credit) => (
|
|
137
138
|
<div key={credit.code} className="flex justify-between text-sm text-green-600">
|
|
138
139
|
<span>{t('storeCredit', { code: maskCode(credit.code) })}</span>
|
|
139
140
|
<span>
|
|
@@ -160,7 +161,9 @@ export async function OrderSummary({ activeOrder }: { activeOrder: ActiveOrder }
|
|
|
160
161
|
/>
|
|
161
162
|
</span>
|
|
162
163
|
<Price
|
|
163
|
-
value={
|
|
164
|
+
value={
|
|
165
|
+
activeOrder.totalWithTaxAfterStoreCredit ?? activeOrder.totalWithTax
|
|
166
|
+
}
|
|
164
167
|
currencyCode={activeOrder.currencyCode}
|
|
165
168
|
taxLabel="incl"
|
|
166
169
|
/>
|
|
@@ -15,8 +15,8 @@ const MAX_STORE_CREDITS_IN_CART = 100;
|
|
|
15
15
|
type ActiveOrder = {
|
|
16
16
|
id: string;
|
|
17
17
|
couponCodes?: string[] | null;
|
|
18
|
-
storeCreditsApplied
|
|
19
|
-
totalWithTaxAfterStoreCredit
|
|
18
|
+
storeCreditsApplied?: Array<{ code: string; balance: number; amountDeducted: number }>;
|
|
19
|
+
totalWithTaxAfterStoreCredit?: number;
|
|
20
20
|
currencyCode: string;
|
|
21
21
|
};
|
|
22
22
|
|
|
@@ -27,11 +27,14 @@ export async function PromotionCode({ activeOrder }: { activeOrder: ActiveOrder
|
|
|
27
27
|
client.query(GetMyStoreCreditsQuery, { options: { take: MAX_STORE_CREDITS_IN_CART } }),
|
|
28
28
|
]);
|
|
29
29
|
const availableCredits = creditsError ? [] : creditsResult.data.myStoreCredits?.items ?? [];
|
|
30
|
-
const
|
|
30
|
+
const storeCreditsApplied = activeOrder.storeCreditsApplied ?? [];
|
|
31
|
+
const appliedCodes = new Set(storeCreditsApplied.map((c) => c.code));
|
|
31
32
|
const activeCustomerId = activeCustomer?.id;
|
|
32
33
|
// Only show credits the current customer can actually redeem (own + unclaimed)
|
|
33
34
|
// Hide when order is already fully covered by applied credits
|
|
34
|
-
const orderFullyCovered =
|
|
35
|
+
const orderFullyCovered =
|
|
36
|
+
activeOrder.totalWithTaxAfterStoreCredit !== undefined &&
|
|
37
|
+
activeOrder.totalWithTaxAfterStoreCredit <= 0;
|
|
35
38
|
const redeemableCredits = orderFullyCovered
|
|
36
39
|
? []
|
|
37
40
|
: availableCredits.filter(
|
|
@@ -77,9 +80,9 @@ export async function PromotionCode({ activeOrder }: { activeOrder: ActiveOrder
|
|
|
77
80
|
)}
|
|
78
81
|
|
|
79
82
|
{/* Applied store credits */}
|
|
80
|
-
{
|
|
83
|
+
{storeCreditsApplied.length > 0 && (
|
|
81
84
|
<div className="space-y-2">
|
|
82
|
-
{
|
|
85
|
+
{storeCreditsApplied.map((credit) => (
|
|
83
86
|
<div
|
|
84
87
|
key={credit.code}
|
|
85
88
|
className="flex items-center justify-between p-3 border rounded-md bg-green-50 dark:bg-green-950/20"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFragment } from '@/graphql';
|
|
2
2
|
import { redirect } from '@/i18n/navigation';
|
|
3
3
|
import { type Locale } from '@/i18n/routing';
|
|
4
|
+
import { pinRootLocale } from '@/i18n/server';
|
|
4
5
|
import { noIndexRobots } from '@/lib/metadata';
|
|
5
6
|
import { getActiveCustomer, getCompanyAccount } from '@/lib/vendure/actions';
|
|
6
7
|
import { client } from '@/lib/vendure/api';
|
|
@@ -145,6 +146,7 @@ async function CheckoutContent({ params }: { params: Promise<{ locale: string }>
|
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
export default async function CheckoutPage({ params }: { params: Promise<{ locale: string }> }) {
|
|
149
|
+
await pinRootLocale();
|
|
148
150
|
const t = await getTranslations('Checkout');
|
|
149
151
|
return (
|
|
150
152
|
<div className="container mx-auto px-4 py-8 mt-[var(--page-top-offset)]">
|
|
@@ -15,9 +15,9 @@ import { getMessages, setRequestLocale } from 'next-intl/server';
|
|
|
15
15
|
import { Geist, Geist_Mono, Inter_Tight, JetBrains_Mono } from 'next/font/google';
|
|
16
16
|
import { Suspense } from 'react';
|
|
17
17
|
import './globals.css';
|
|
18
|
-
// next/root-params provides cache-safe locale resolution.
|
|
19
|
-
//
|
|
20
|
-
//
|
|
18
|
+
// next/root-params provides cache-safe locale resolution. There must be no
|
|
19
|
+
// layout.tsx above [locale], or [locale] stops being a root param — see
|
|
20
|
+
// next.config.ts.
|
|
21
21
|
import { toOgLocale } from '@/i18n/locale-utils';
|
|
22
22
|
import { routing, type Locale } from '@/i18n/routing';
|
|
23
23
|
import { hasLocale } from 'next-intl';
|
package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/order-confirmation.tsx
CHANGED
|
@@ -2,78 +2,20 @@ import { Price } from '@/components/commerce/price';
|
|
|
2
2
|
import { Button } from '@/components/ui/button';
|
|
3
3
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
4
4
|
import { Separator } from '@/components/ui/separator';
|
|
5
|
-
import { graphql } from '@/graphql';
|
|
6
5
|
import { Link } from '@/i18n/navigation';
|
|
7
6
|
import { maskCode } from '@/lib/format';
|
|
8
7
|
import { client } from '@/lib/vendure/api';
|
|
9
8
|
import { getLatestApprovalForOrder } from '@/lib/vendure/approval-utils';
|
|
9
|
+
import { GetOrderByCodeForConfirmationQuery } from '@/lib/vendure/queries';
|
|
10
10
|
import { CheckCircle2, Clock, MessageSquare, XCircle } from 'lucide-react';
|
|
11
11
|
import { getTranslations } from 'next-intl/server';
|
|
12
12
|
import Image from 'next/image';
|
|
13
13
|
import { notFound } from 'next/navigation';
|
|
14
14
|
|
|
15
|
-
const GetOrderByCodeQuery = graphql(`
|
|
16
|
-
query GetOrderByCode($code: String!) {
|
|
17
|
-
orderByCode(code: $code) {
|
|
18
|
-
id
|
|
19
|
-
code
|
|
20
|
-
state
|
|
21
|
-
subTotal
|
|
22
|
-
subTotalWithTax
|
|
23
|
-
shipping
|
|
24
|
-
shippingWithTax
|
|
25
|
-
total
|
|
26
|
-
totalWithTax
|
|
27
|
-
currencyCode
|
|
28
|
-
storeCreditsApplied {
|
|
29
|
-
code
|
|
30
|
-
balance
|
|
31
|
-
amountDeducted
|
|
32
|
-
}
|
|
33
|
-
totalWithTaxAfterStoreCredit
|
|
34
|
-
payments {
|
|
35
|
-
id
|
|
36
|
-
method
|
|
37
|
-
amount
|
|
38
|
-
state
|
|
39
|
-
metadata
|
|
40
|
-
}
|
|
41
|
-
lines {
|
|
42
|
-
id
|
|
43
|
-
productVariant {
|
|
44
|
-
id
|
|
45
|
-
name
|
|
46
|
-
product {
|
|
47
|
-
id
|
|
48
|
-
name
|
|
49
|
-
slug
|
|
50
|
-
featuredAsset {
|
|
51
|
-
id
|
|
52
|
-
preview
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
quantity
|
|
57
|
-
linePrice
|
|
58
|
-
linePriceWithTax
|
|
59
|
-
}
|
|
60
|
-
shippingAddress {
|
|
61
|
-
fullName
|
|
62
|
-
streetLine1
|
|
63
|
-
streetLine2
|
|
64
|
-
city
|
|
65
|
-
province
|
|
66
|
-
postalCode
|
|
67
|
-
country
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
`);
|
|
72
|
-
|
|
73
15
|
export async function OrderConfirmation({ params }: { params: Promise<{ locale: string; code: string }> }) {
|
|
74
16
|
const { code } = await params;
|
|
75
17
|
const t = await getTranslations('OrderConfirmation');
|
|
76
|
-
const [result, queryError] = await client.query(
|
|
18
|
+
const [result, queryError] = await client.query(GetOrderByCodeForConfirmationQuery, { code });
|
|
77
19
|
if (queryError) {
|
|
78
20
|
notFound();
|
|
79
21
|
}
|
|
@@ -4,6 +4,7 @@ import { Price } from '@/components/commerce/price';
|
|
|
4
4
|
import { Badge } from '@/components/ui/badge';
|
|
5
5
|
import { FragmentOf, readFragment } from '@/graphql';
|
|
6
6
|
import { Link } from '@/i18n/navigation';
|
|
7
|
+
import { getSearchHighlights } from '@/lib/vendure/capability-ui';
|
|
7
8
|
import { ProductCardFragment } from '@/lib/vendure/fragments';
|
|
8
9
|
import { ChevronDown, ChevronUp } from 'lucide-react';
|
|
9
10
|
import Image from 'next/image';
|
|
@@ -19,6 +20,7 @@ interface ProductCardListProps {
|
|
|
19
20
|
|
|
20
21
|
export function ProductCardList({ product: productProp, isExpanded, onToggle }: ProductCardListProps) {
|
|
21
22
|
const product = readFragment(ProductCardFragment, productProp);
|
|
23
|
+
const highlights = getSearchHighlights(product);
|
|
22
24
|
const prefetchedRef = useRef(false);
|
|
23
25
|
|
|
24
26
|
const handleMouseEnter = () => {
|
|
@@ -70,10 +72,10 @@ export function ProductCardList({ product: productProp, isExpanded, onToggle }:
|
|
|
70
72
|
>
|
|
71
73
|
{product.productName}
|
|
72
74
|
</Link>
|
|
73
|
-
{
|
|
75
|
+
{highlights.length > 0 && highlights[0].matchedTokens.length > 0 && (
|
|
74
76
|
<HighlightedText
|
|
75
77
|
text={product.productName}
|
|
76
|
-
matchedTokens={
|
|
78
|
+
matchedTokens={highlights[0].matchedTokens}
|
|
77
79
|
className="text-xs text-muted-foreground line-clamp-1 mt-0.5 red"
|
|
78
80
|
/>
|
|
79
81
|
)}
|
|
@@ -2,6 +2,7 @@ import { Price } from '@/components/commerce/price';
|
|
|
2
2
|
import { PrefetchLink } from '@/components/shared/prefetch-link';
|
|
3
3
|
import { Badge } from '@/components/ui/badge';
|
|
4
4
|
import { FragmentOf, readFragment } from '@/graphql';
|
|
5
|
+
import { getSearchHighlights } from '@/lib/vendure/capability-ui';
|
|
5
6
|
import { ProductCardFragment } from '@/lib/vendure/fragments';
|
|
6
7
|
import { Package } from 'lucide-react';
|
|
7
8
|
import { useTranslations } from 'next-intl';
|
|
@@ -15,6 +16,7 @@ interface ProductCardProps {
|
|
|
15
16
|
|
|
16
17
|
export function ProductCard({ product: productProp }: ProductCardProps) {
|
|
17
18
|
const product = readFragment(ProductCardFragment, productProp);
|
|
19
|
+
const highlights = getSearchHighlights(product);
|
|
18
20
|
const t = useTranslations('Product');
|
|
19
21
|
|
|
20
22
|
return (
|
|
@@ -62,10 +64,10 @@ export function ProductCard({ product: productProp }: ProductCardProps) {
|
|
|
62
64
|
<h3 className="font-medium text-sm line-clamp-2 leading-snug group-hover:text-primary transition-colors">
|
|
63
65
|
{product.productName}
|
|
64
66
|
</h3>
|
|
65
|
-
{
|
|
67
|
+
{highlights.length > 0 && highlights[0].matchedTokens.length > 0 && (
|
|
66
68
|
<HighlightedText
|
|
67
69
|
text={product.productName}
|
|
68
|
-
matchedTokens={
|
|
70
|
+
matchedTokens={highlights[0].matchedTokens}
|
|
69
71
|
className="text-xs text-muted-foreground line-clamp-2"
|
|
70
72
|
/>
|
|
71
73
|
)}
|
|
@@ -23,9 +23,24 @@ interface QuoteActionButtonsProps {
|
|
|
23
23
|
quoteId: string;
|
|
24
24
|
status: string;
|
|
25
25
|
convertedOrderId?: string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Whether the deployment is granted `quote.negotiation`.
|
|
28
|
+
*
|
|
29
|
+
* Resolved by the server component that renders this one. Reading the
|
|
30
|
+
* capability hint here instead would inline the deployment's entire granted
|
|
31
|
+
* capability list into this client chunk, because Next.js replaces
|
|
32
|
+
* `process.env.NEXT_PUBLIC_*` with its literal value wherever the module
|
|
33
|
+
* graph reaches the browser.
|
|
34
|
+
*/
|
|
35
|
+
hasQuoteNegotiation: boolean;
|
|
26
36
|
}
|
|
27
37
|
|
|
28
|
-
export function QuoteActionButtons({
|
|
38
|
+
export function QuoteActionButtons({
|
|
39
|
+
quoteId,
|
|
40
|
+
status,
|
|
41
|
+
convertedOrderId,
|
|
42
|
+
hasQuoteNegotiation,
|
|
43
|
+
}: QuoteActionButtonsProps) {
|
|
29
44
|
const router = useRouter();
|
|
30
45
|
const t = useTranslations('Quotes');
|
|
31
46
|
const [reason, setReason] = useState('');
|
|
@@ -174,7 +189,7 @@ export function QuoteActionButtons({ quoteId, status, convertedOrderId }: QuoteA
|
|
|
174
189
|
)}
|
|
175
190
|
{!stockWarning && (
|
|
176
191
|
<>
|
|
177
|
-
{status === 'QUOTED' && (
|
|
192
|
+
{status === 'QUOTED' && hasQuoteNegotiation && (
|
|
178
193
|
<Textarea
|
|
179
194
|
placeholder={t('changeRequestPlaceholder')}
|
|
180
195
|
value={reason}
|
|
@@ -213,18 +228,20 @@ export function QuoteActionButtons({ quoteId, status, convertedOrderId }: QuoteA
|
|
|
213
228
|
</Button>
|
|
214
229
|
{status === 'QUOTED' && (
|
|
215
230
|
<>
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
231
|
+
{hasQuoteNegotiation && (
|
|
232
|
+
<Button
|
|
233
|
+
variant="outline"
|
|
234
|
+
size="sm"
|
|
235
|
+
onClick={() =>
|
|
236
|
+
handleAction(() => requestQuoteChanges(quoteId, reason))
|
|
237
|
+
}
|
|
238
|
+
disabled={isPending || !reason.trim()}
|
|
239
|
+
className="w-full"
|
|
240
|
+
>
|
|
241
|
+
<MessageSquare className="h-4 w-4 mr-1" />
|
|
242
|
+
{t('actionRequestChanges')}
|
|
243
|
+
</Button>
|
|
244
|
+
)}
|
|
228
245
|
<Button
|
|
229
246
|
variant="destructive"
|
|
230
247
|
size="sm"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time capability set for the storefront.
|
|
3
|
+
*
|
|
4
|
+
* The server composes its GraphQL schema from the capability modules the
|
|
5
|
+
* deployment is entitled to, so a document that selects a field belonging to an
|
|
6
|
+
* absent module fails validation on every request. The storefront therefore has
|
|
7
|
+
* to know which capability-gated fields it may select before it builds its
|
|
8
|
+
* documents.
|
|
9
|
+
*
|
|
10
|
+
* `NEXT_PUBLIC_VENDURE_CAPABILITIES` is a comma-separated list of the capability
|
|
11
|
+
* keys the target deployment is granted, baked at build time like
|
|
12
|
+
* `NEXT_PUBLIC_SCENARIO`. It is a composition hint, not an authorization
|
|
13
|
+
* boundary: the server remains authoritative and rejects anything this list
|
|
14
|
+
* gets wrong.
|
|
15
|
+
*
|
|
16
|
+
* When the variable is unset every capability is assumed, which keeps existing
|
|
17
|
+
* full-composition deployments building and running unchanged.
|
|
18
|
+
*/
|
|
19
|
+
const RAW_CAPABILITIES = process.env.NEXT_PUBLIC_VENDURE_CAPABILITIES;
|
|
20
|
+
|
|
21
|
+
function parseCapabilities(raw: string | undefined): ReadonlySet<string> | null {
|
|
22
|
+
if (raw == null) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return new Set(
|
|
26
|
+
raw
|
|
27
|
+
.split(',')
|
|
28
|
+
.map((key) => key.trim())
|
|
29
|
+
.filter((key) => key.length > 0),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const GRANTED = parseCapabilities(RAW_CAPABILITIES);
|
|
34
|
+
|
|
35
|
+
/** True when the deployment is expected to expose `key`'s schema. */
|
|
36
|
+
export function hasCapability(key: string): boolean {
|
|
37
|
+
return GRANTED == null || GRANTED.has(key);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Configurator forms, rules, saved configurations, and the configured order journey. */
|
|
41
|
+
export const hasConfiguratorForms = hasCapability('configurator.forms');
|