@vendure/dashboard 3.3.2 → 3.3.3
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/dist/plugin/utils/config-loader.d.ts +12 -1
- package/dist/plugin/utils/config-loader.js +25 -7
- package/dist/plugin/vite-plugin-vendure-dashboard.d.ts +8 -0
- package/dist/plugin/vite-plugin-vendure-dashboard.js +5 -1
- package/package.json +4 -4
- package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +1 -4
- package/src/app/routes/_authenticated/_channels/channels.tsx +18 -0
- package/src/app/routes/_authenticated/_channels/channels_.$id.tsx +1 -5
- package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +1 -4
- package/src/app/routes/_authenticated/_countries/countries_.$id.tsx +1 -4
- package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +1 -4
- package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +1 -5
- package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +56 -74
- package/src/app/routes/_authenticated/_products/components/add-product-variant-dialog.tsx +369 -0
- package/src/app/routes/_authenticated/_products/components/create-product-options-dialog.tsx +435 -0
- package/src/app/routes/_authenticated/_products/components/product-option-select.tsx +117 -0
- package/src/app/routes/_authenticated/_products/components/product-variants-table.tsx +4 -2
- package/src/app/routes/_authenticated/_products/products_.$id.tsx +17 -3
- package/src/app/routes/_authenticated/_profile/profile.tsx +1 -4
- package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +1 -4
- package/src/lib/components/data-table/data-table-view-options.tsx +12 -2
- package/src/lib/components/data-table/data-table.tsx +9 -0
- package/src/lib/components/layout/channel-switcher.tsx +1 -2
- package/src/lib/components/shared/assigned-facet-values.tsx +13 -14
- package/src/lib/components/shared/entity-assets.tsx +140 -70
- package/src/lib/components/shared/paginated-list-data-table.tsx +10 -0
- package/src/lib/components/ui/button.tsx +1 -1
- package/src/lib/framework/form-engine/use-generated-form.tsx +1 -0
- package/src/lib/framework/page/list-page.tsx +2 -2
- package/src/lib/framework/page/use-detail-page.ts +7 -0
- package/src/lib/graphql/api.ts +10 -1
- package/src/lib/hooks/use-permissions.ts +4 -4
- package/src/lib/providers/auth.tsx +9 -3
- package/src/lib/providers/channel-provider.tsx +64 -24
- package/src/lib/providers/server-config.tsx +2 -2
- package/vite/utils/config-loader.ts +48 -13
- package/vite/vite-plugin-vendure-dashboard.ts +14 -4
|
@@ -14,6 +14,7 @@ export interface ConfigLoaderOptions {
|
|
|
14
14
|
tempDir: string;
|
|
15
15
|
vendureConfigExport?: string;
|
|
16
16
|
logger?: Logger;
|
|
17
|
+
reportCompilationErrors?: boolean;
|
|
17
18
|
}
|
|
18
19
|
export interface LoadVendureConfigResult {
|
|
19
20
|
vendureConfig: VendureConfig;
|
|
@@ -37,5 +38,15 @@ export interface LoadVendureConfigResult {
|
|
|
37
38
|
* to handle the compiled JavaScript output.
|
|
38
39
|
*/
|
|
39
40
|
export declare function loadVendureConfig(options: ConfigLoaderOptions): Promise<LoadVendureConfigResult>;
|
|
40
|
-
|
|
41
|
+
type CompileFileOptions = {
|
|
42
|
+
inputRootDir: string;
|
|
43
|
+
inputPath: string;
|
|
44
|
+
outputDir: string;
|
|
45
|
+
logger?: Logger;
|
|
46
|
+
compiledFiles?: Set<string>;
|
|
47
|
+
isRoot?: boolean;
|
|
48
|
+
pluginInfo?: PluginInfo[];
|
|
49
|
+
reportCompilationErrors?: boolean;
|
|
50
|
+
};
|
|
51
|
+
export declare function compileFile({ inputRootDir, inputPath, outputDir, logger, compiledFiles, isRoot, pluginInfo, reportCompilationErrors, }: CompileFileOptions): Promise<PluginInfo[]>;
|
|
41
52
|
export {};
|
|
@@ -38,7 +38,12 @@ export async function loadVendureConfig(options) {
|
|
|
38
38
|
const configFileName = path.basename(vendureConfigPath);
|
|
39
39
|
const inputRootDir = path.dirname(vendureConfigPath);
|
|
40
40
|
await fs.remove(outputPath);
|
|
41
|
-
const pluginInfo = await compileFile(
|
|
41
|
+
const pluginInfo = await compileFile({
|
|
42
|
+
inputRootDir,
|
|
43
|
+
inputPath: vendureConfigPath,
|
|
44
|
+
outputDir: outputPath,
|
|
45
|
+
logger,
|
|
46
|
+
});
|
|
42
47
|
const compiledConfigFilePath = pathToFileURL(path.join(outputPath, configFileName)).href.replace(/.ts$/, '.js');
|
|
43
48
|
// create package.json with type commonjs and save it to the output dir
|
|
44
49
|
await fs.writeFile(path.join(outputPath, 'package.json'), JSON.stringify({ type: 'commonjs' }, null, 2));
|
|
@@ -93,7 +98,10 @@ async function findTsConfigPaths(configPath, logger) {
|
|
|
93
98
|
// Normalize slashes for consistency, keep relative
|
|
94
99
|
pattern.replace(/\\/g, '/'));
|
|
95
100
|
}
|
|
96
|
-
logger.debug(`Found tsconfig paths in ${tsConfigPath}: ${JSON.stringify({
|
|
101
|
+
logger.debug(`Found tsconfig paths in ${tsConfigPath}: ${JSON.stringify({
|
|
102
|
+
baseUrl: tsConfigBaseUrl,
|
|
103
|
+
paths,
|
|
104
|
+
}, null, 2)}`);
|
|
97
105
|
return { baseUrl: tsConfigBaseUrl, paths };
|
|
98
106
|
}
|
|
99
107
|
}
|
|
@@ -111,7 +119,7 @@ async function findTsConfigPaths(configPath, logger) {
|
|
|
111
119
|
logger.debug(`No tsconfig paths found traversing up from ${configDir}`);
|
|
112
120
|
return undefined;
|
|
113
121
|
}
|
|
114
|
-
export async function compileFile(inputRootDir, inputPath, outputDir, logger = defaultLogger, compiledFiles = new Set(), isRoot = true, pluginInfo = []) {
|
|
122
|
+
export async function compileFile({ inputRootDir, inputPath, outputDir, logger = defaultLogger, compiledFiles = new Set(), isRoot = true, pluginInfo = [], reportCompilationErrors = false, }) {
|
|
115
123
|
const absoluteInputPath = path.resolve(inputPath);
|
|
116
124
|
if (compiledFiles.has(absoluteInputPath)) {
|
|
117
125
|
return pluginInfo;
|
|
@@ -250,7 +258,15 @@ export async function compileFile(inputRootDir, inputPath, outputDir, logger = d
|
|
|
250
258
|
// Recursively collect all files that need to be compiled
|
|
251
259
|
for (const importPath of importPaths) {
|
|
252
260
|
// Pass rootTsConfigInfo down, but set isRoot to false
|
|
253
|
-
await compileFile(
|
|
261
|
+
await compileFile({
|
|
262
|
+
inputRootDir,
|
|
263
|
+
inputPath: importPath,
|
|
264
|
+
outputDir,
|
|
265
|
+
logger,
|
|
266
|
+
compiledFiles,
|
|
267
|
+
isRoot: false,
|
|
268
|
+
pluginInfo,
|
|
269
|
+
});
|
|
254
270
|
}
|
|
255
271
|
// If this is the root file (the one that started the compilation),
|
|
256
272
|
// use the TypeScript compiler API to compile all files together
|
|
@@ -289,9 +305,11 @@ export async function compileFile(inputRootDir, inputPath, outputDir, logger = d
|
|
|
289
305
|
const program = ts.createProgram(allFiles, compilerOptions);
|
|
290
306
|
logger.info(`Emitting compiled files to ${outputDir}`);
|
|
291
307
|
const emitResult = program.emit();
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
308
|
+
if (reportCompilationErrors) {
|
|
309
|
+
const hasEmitErrors = reportDiagnostics(program, emitResult, logger);
|
|
310
|
+
if (hasEmitErrors) {
|
|
311
|
+
throw new Error('TypeScript compilation failed with errors.');
|
|
312
|
+
}
|
|
295
313
|
}
|
|
296
314
|
logger.info(`Successfully compiled ${allFiles.length} files to ${outputDir}`);
|
|
297
315
|
}
|
|
@@ -24,6 +24,14 @@ export type VitePluginVendureDashboardOptions = {
|
|
|
24
24
|
gqlTadaOutputPath?: string;
|
|
25
25
|
tempCompilationDir?: string;
|
|
26
26
|
disableTansStackRouterPlugin?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* @description
|
|
29
|
+
* If set to `true`, compilation errors during the build process will be reported and
|
|
30
|
+
* the build will fail.
|
|
31
|
+
*
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
reportCompilationErrors?: boolean;
|
|
27
35
|
} & UiConfigPluginOptions & ThemeVariablesPluginOptions;
|
|
28
36
|
/**
|
|
29
37
|
* @description
|
|
@@ -42,7 +42,11 @@ export function vendureDashboardPlugin(options) {
|
|
|
42
42
|
}),
|
|
43
43
|
themeVariablesPlugin({ theme: options.theme }),
|
|
44
44
|
tailwindcss(),
|
|
45
|
-
configLoaderPlugin({
|
|
45
|
+
configLoaderPlugin({
|
|
46
|
+
vendureConfigPath: normalizedVendureConfigPath,
|
|
47
|
+
tempDir,
|
|
48
|
+
reportCompilationErrors: options.reportCompilationErrors,
|
|
49
|
+
}),
|
|
46
50
|
viteConfigPlugin({ packageRoot }),
|
|
47
51
|
adminApiSchemaPlugin(),
|
|
48
52
|
dashboardMetadataPlugin({ rootDir: tempDir }),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vendure/dashboard",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "3.3.
|
|
4
|
+
"version": "3.3.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
"@types/react-dom": "^19.0.4",
|
|
87
87
|
"@types/react-grid-layout": "^1.3.5",
|
|
88
88
|
"@uidotdev/usehooks": "^2.4.1",
|
|
89
|
-
"@vendure/common": "3.3.
|
|
90
|
-
"@vendure/core": "3.3.
|
|
89
|
+
"@vendure/common": "3.3.3",
|
|
90
|
+
"@vendure/core": "3.3.3",
|
|
91
91
|
"@vitejs/plugin-react": "^4.3.4",
|
|
92
92
|
"awesome-graphql-client": "^2.1.0",
|
|
93
93
|
"class-variance-authority": "^0.7.1",
|
|
@@ -130,5 +130,5 @@
|
|
|
130
130
|
"lightningcss-linux-arm64-musl": "^1.29.3",
|
|
131
131
|
"lightningcss-linux-x64-musl": "^1.29.1"
|
|
132
132
|
},
|
|
133
|
-
"gitHead": "
|
|
133
|
+
"gitHead": "56068550360311cb70b04a8aba2743a1387787ac"
|
|
134
134
|
}
|
|
@@ -70,9 +70,7 @@ function AdministratorDetailPage() {
|
|
|
70
70
|
},
|
|
71
71
|
params: { id: params.id },
|
|
72
72
|
onSuccess: async data => {
|
|
73
|
-
toast(i18n.t('Successfully updated administrator')
|
|
74
|
-
position: 'top-right',
|
|
75
|
-
});
|
|
73
|
+
toast(i18n.t('Successfully updated administrator'));
|
|
76
74
|
resetForm();
|
|
77
75
|
if (creatingNewEntity) {
|
|
78
76
|
await navigate({ to: `../$id`, params: { id: data.id } });
|
|
@@ -80,7 +78,6 @@ function AdministratorDetailPage() {
|
|
|
80
78
|
},
|
|
81
79
|
onError: err => {
|
|
82
80
|
toast(i18n.t('Failed to update administrator'), {
|
|
83
|
-
position: 'top-right',
|
|
84
81
|
description: err instanceof Error ? err.message : 'Unknown error',
|
|
85
82
|
});
|
|
86
83
|
},
|
|
@@ -8,6 +8,7 @@ import { Trans } from '@/lib/trans.js';
|
|
|
8
8
|
import { createFileRoute, Link } from '@tanstack/react-router';
|
|
9
9
|
import { PlusIcon } from 'lucide-react';
|
|
10
10
|
import { channelListQuery, deleteChannelDocument } from './channels.graphql.js';
|
|
11
|
+
import { useLocalFormat } from '@/hooks/use-local-format.js';
|
|
11
12
|
|
|
12
13
|
export const Route = createFileRoute('/_authenticated/_channels/channels')({
|
|
13
14
|
component: ChannelListPage,
|
|
@@ -15,6 +16,7 @@ export const Route = createFileRoute('/_authenticated/_channels/channels')({
|
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
function ChannelListPage() {
|
|
19
|
+
const { formatLanguageName } = useLocalFormat();
|
|
18
20
|
return (
|
|
19
21
|
<ListPage
|
|
20
22
|
pageId="channel-list"
|
|
@@ -25,6 +27,10 @@ function ChannelListPage() {
|
|
|
25
27
|
defaultVisibility={{
|
|
26
28
|
code: true,
|
|
27
29
|
token: true,
|
|
30
|
+
availableCurrencyCodes: false,
|
|
31
|
+
availableLanguageCodes: false,
|
|
32
|
+
defaultTaxZone: false,
|
|
33
|
+
defaultShippingZone: false,
|
|
28
34
|
}}
|
|
29
35
|
onSearchTermChange={searchTerm => {
|
|
30
36
|
return {
|
|
@@ -43,6 +49,18 @@ function ChannelListPage() {
|
|
|
43
49
|
);
|
|
44
50
|
},
|
|
45
51
|
},
|
|
52
|
+
seller: {
|
|
53
|
+
header: 'Seller',
|
|
54
|
+
cell: ({ row }) => {
|
|
55
|
+
return row.original.seller?.name;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
defaultLanguageCode: {
|
|
59
|
+
header: 'Default Language',
|
|
60
|
+
cell: ({ row }) => {
|
|
61
|
+
return formatLanguageName(row.original.defaultLanguageCode);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
46
64
|
}}
|
|
47
65
|
>
|
|
48
66
|
<PageActionBarRight>
|
|
@@ -76,23 +76,19 @@ function ChannelDetailPage() {
|
|
|
76
76
|
params: { id: params.id },
|
|
77
77
|
onSuccess: async data => {
|
|
78
78
|
if (data.__typename === 'Channel') {
|
|
79
|
-
toast(i18n.t('Successfully updated channel')
|
|
80
|
-
position: 'top-right',
|
|
81
|
-
});
|
|
79
|
+
toast(i18n.t('Successfully updated channel'));
|
|
82
80
|
resetForm();
|
|
83
81
|
if (creatingNewEntity) {
|
|
84
82
|
await navigate({ to: `../$id`, params: { id: data.id } });
|
|
85
83
|
}
|
|
86
84
|
} else {
|
|
87
85
|
toast(i18n.t('Failed to update channel'), {
|
|
88
|
-
position: 'top-right',
|
|
89
86
|
description: data.message,
|
|
90
87
|
});
|
|
91
88
|
}
|
|
92
89
|
},
|
|
93
90
|
onError: err => {
|
|
94
91
|
toast(i18n.t('Failed to update channel'), {
|
|
95
|
-
position: 'top-right',
|
|
96
92
|
description: err instanceof Error ? err.message : 'Unknown error',
|
|
97
93
|
});
|
|
98
94
|
},
|
|
@@ -85,9 +85,7 @@ function CollectionDetailPage() {
|
|
|
85
85
|
},
|
|
86
86
|
params: { id: params.id },
|
|
87
87
|
onSuccess: async data => {
|
|
88
|
-
toast(i18n.t('Successfully updated collection')
|
|
89
|
-
position: 'top-right',
|
|
90
|
-
});
|
|
88
|
+
toast(i18n.t('Successfully updated collection'));
|
|
91
89
|
resetForm();
|
|
92
90
|
if (creatingNewEntity) {
|
|
93
91
|
await navigate({ to: `../$id`, params: { id: data.id } });
|
|
@@ -95,7 +93,6 @@ function CollectionDetailPage() {
|
|
|
95
93
|
},
|
|
96
94
|
onError: err => {
|
|
97
95
|
toast(i18n.t('Failed to update collection'), {
|
|
98
|
-
position: 'top-right',
|
|
99
96
|
description: err instanceof Error ? err.message : 'Unknown error',
|
|
100
97
|
});
|
|
101
98
|
},
|
|
@@ -57,9 +57,7 @@ function CountryDetailPage() {
|
|
|
57
57
|
},
|
|
58
58
|
params: { id: params.id },
|
|
59
59
|
onSuccess: async data => {
|
|
60
|
-
toast(i18n.t('Successfully updated country')
|
|
61
|
-
position: 'top-right',
|
|
62
|
-
});
|
|
60
|
+
toast(i18n.t('Successfully updated country'));
|
|
63
61
|
form.reset(form.getValues());
|
|
64
62
|
if (creatingNewEntity) {
|
|
65
63
|
await navigate({ to: `../$id`, params: { id: data.id } });
|
|
@@ -67,7 +65,6 @@ function CountryDetailPage() {
|
|
|
67
65
|
},
|
|
68
66
|
onError: err => {
|
|
69
67
|
toast(i18n.t('Failed to update country'), {
|
|
70
|
-
position: 'top-right',
|
|
71
68
|
description: err instanceof Error ? err.message : 'Unknown error',
|
|
72
69
|
});
|
|
73
70
|
},
|
|
@@ -68,9 +68,7 @@ function FacetDetailPage() {
|
|
|
68
68
|
},
|
|
69
69
|
params: { id: params.id },
|
|
70
70
|
onSuccess: async data => {
|
|
71
|
-
toast(i18n.t('Successfully updated facet')
|
|
72
|
-
position: 'top-right',
|
|
73
|
-
});
|
|
71
|
+
toast(i18n.t('Successfully updated facet'));
|
|
74
72
|
resetForm();
|
|
75
73
|
if (creatingNewEntity) {
|
|
76
74
|
await navigate({ to: `../$id`, params: { id: data.id } });
|
|
@@ -78,7 +76,6 @@ function FacetDetailPage() {
|
|
|
78
76
|
},
|
|
79
77
|
onError: err => {
|
|
80
78
|
toast(i18n.t('Failed to update facet'), {
|
|
81
|
-
position: 'top-right',
|
|
82
79
|
description: err instanceof Error ? err.message : 'Unknown error',
|
|
83
80
|
});
|
|
84
81
|
},
|
|
@@ -59,23 +59,19 @@ function GlobalSettingsPage() {
|
|
|
59
59
|
params: { id: 'undefined' },
|
|
60
60
|
onSuccess: async data => {
|
|
61
61
|
if (data.__typename === 'GlobalSettings') {
|
|
62
|
-
toast(i18n.t('Successfully updated global settings')
|
|
63
|
-
position: 'top-right',
|
|
64
|
-
});
|
|
62
|
+
toast(i18n.t('Successfully updated global settings'));
|
|
65
63
|
form.reset(form.getValues());
|
|
66
64
|
if (creatingNewEntity) {
|
|
67
65
|
await navigate({ to: `../$id`, params: { id: data.id } });
|
|
68
66
|
}
|
|
69
67
|
} else {
|
|
70
68
|
toast(i18n.t('Failed to update global settings'), {
|
|
71
|
-
position: 'top-right',
|
|
72
69
|
description: data.message,
|
|
73
70
|
});
|
|
74
71
|
}
|
|
75
72
|
},
|
|
76
73
|
onError: err => {
|
|
77
74
|
toast(i18n.t('Failed to update global settings'), {
|
|
78
|
-
position: 'top-right',
|
|
79
75
|
description: err instanceof Error ? err.message : 'Unknown error',
|
|
80
76
|
});
|
|
81
77
|
},
|
|
@@ -7,14 +7,7 @@ import { PermissionGuard } from '@/components/shared/permission-guard.js';
|
|
|
7
7
|
import { TaxCategorySelector } from '@/components/shared/tax-category-selector.js';
|
|
8
8
|
import { TranslatableFormFieldWrapper } from '@/components/shared/translatable-form-field.js';
|
|
9
9
|
import { Button } from '@/components/ui/button.js';
|
|
10
|
-
import {
|
|
11
|
-
FormControl,
|
|
12
|
-
FormDescription,
|
|
13
|
-
FormField,
|
|
14
|
-
FormItem,
|
|
15
|
-
FormLabel,
|
|
16
|
-
FormMessage,
|
|
17
|
-
} from '@/components/ui/form.js';
|
|
10
|
+
import { FormControl, FormDescription, FormItem, FormLabel, FormMessage } from '@/components/ui/form.js';
|
|
18
11
|
import { Input } from '@/components/ui/input.js';
|
|
19
12
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select.js';
|
|
20
13
|
import { Switch } from '@/components/ui/switch.js';
|
|
@@ -194,17 +187,15 @@ function ProductVariantDetailPage() {
|
|
|
194
187
|
<FormFieldWrapper
|
|
195
188
|
control={form.control}
|
|
196
189
|
name={`stockLevels.${index}.stockOnHand`}
|
|
190
|
+
label={<Trans>Stock level</Trans>}
|
|
197
191
|
render={({ field }) => (
|
|
198
|
-
<
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}} />
|
|
206
|
-
</FormControl>
|
|
207
|
-
</FormItem>
|
|
192
|
+
<Input
|
|
193
|
+
type="number"
|
|
194
|
+
value={field.value}
|
|
195
|
+
onChange={e => {
|
|
196
|
+
field.onChange(e.target.valueAsNumber);
|
|
197
|
+
}}
|
|
198
|
+
/>
|
|
208
199
|
)}
|
|
209
200
|
/>
|
|
210
201
|
<div>
|
|
@@ -218,77 +209,68 @@ function ProductVariantDetailPage() {
|
|
|
218
209
|
</Fragment>
|
|
219
210
|
))}
|
|
220
211
|
|
|
221
|
-
<
|
|
212
|
+
<FormFieldWrapper
|
|
222
213
|
control={form.control}
|
|
223
214
|
name="trackInventory"
|
|
215
|
+
label={<Trans>Stock levels</Trans>}
|
|
224
216
|
render={({ field }) => (
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
<Trans>Track inventory</Trans>
|
|
228
|
-
</FormLabel>
|
|
229
|
-
<Select onValueChange={val => {
|
|
217
|
+
<Select
|
|
218
|
+
onValueChange={val => {
|
|
230
219
|
if (val) {
|
|
231
|
-
field.onChange(val)
|
|
220
|
+
field.onChange(val);
|
|
232
221
|
}
|
|
233
|
-
}}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
<
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
222
|
+
}}
|
|
223
|
+
value={field.value}
|
|
224
|
+
>
|
|
225
|
+
<FormControl>
|
|
226
|
+
<SelectTrigger className="">
|
|
227
|
+
<SelectValue placeholder="Track inventory" />
|
|
228
|
+
</SelectTrigger>
|
|
229
|
+
</FormControl>
|
|
230
|
+
<SelectContent>
|
|
231
|
+
<SelectItem value="INHERIT">
|
|
232
|
+
<Trans>Inherit from global settings</Trans>
|
|
233
|
+
</SelectItem>
|
|
234
|
+
<SelectItem value="TRUE">
|
|
235
|
+
<Trans>Track</Trans>
|
|
236
|
+
</SelectItem>
|
|
237
|
+
<SelectItem value="FALSE">
|
|
238
|
+
<Trans>Do not track</Trans>
|
|
239
|
+
</SelectItem>
|
|
240
|
+
</SelectContent>
|
|
241
|
+
</Select>
|
|
252
242
|
)}
|
|
253
243
|
/>
|
|
254
|
-
<
|
|
244
|
+
<FormFieldWrapper
|
|
255
245
|
control={form.control}
|
|
256
246
|
name="outOfStockThreshold"
|
|
247
|
+
label={<Trans>Out-of-stock threshold</Trans>}
|
|
248
|
+
description={
|
|
249
|
+
<Trans>
|
|
250
|
+
Sets the stock level at which this variant is considered to be out of
|
|
251
|
+
stock. Using a negative value enables backorder support.
|
|
252
|
+
</Trans>
|
|
253
|
+
}
|
|
257
254
|
render={({ field }) => (
|
|
258
|
-
<
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
<Input type="number" {...field} />
|
|
264
|
-
</FormControl>
|
|
265
|
-
<FormDescription>
|
|
266
|
-
<Trans>
|
|
267
|
-
Sets the stock level at which this variant is considered to be out
|
|
268
|
-
of stock. Using a negative value enables backorder support.
|
|
269
|
-
</Trans>
|
|
270
|
-
</FormDescription>
|
|
271
|
-
</FormItem>
|
|
255
|
+
<Input
|
|
256
|
+
type="number"
|
|
257
|
+
value={field.value}
|
|
258
|
+
onChange={e => field.onChange(e.target.valueAsNumber)}
|
|
259
|
+
/>
|
|
272
260
|
)}
|
|
273
261
|
/>
|
|
274
|
-
<
|
|
262
|
+
<FormFieldWrapper
|
|
275
263
|
control={form.control}
|
|
276
264
|
name="useGlobalOutOfStockThreshold"
|
|
265
|
+
label={<Trans>Use global out-of-stock threshold</Trans>}
|
|
266
|
+
description={
|
|
267
|
+
<Trans>
|
|
268
|
+
Sets the stock level at which this variant is considered to be out of
|
|
269
|
+
stock. Using a negative value enables backorder support.
|
|
270
|
+
</Trans>
|
|
271
|
+
}
|
|
277
272
|
render={({ field }) => (
|
|
278
|
-
<
|
|
279
|
-
<FormLabel>
|
|
280
|
-
<Trans>Use global out-of-stock threshold</Trans>
|
|
281
|
-
</FormLabel>
|
|
282
|
-
<FormControl>
|
|
283
|
-
<Switch checked={field.value} onCheckedChange={field.onChange} />
|
|
284
|
-
</FormControl>
|
|
285
|
-
<FormDescription>
|
|
286
|
-
<Trans>
|
|
287
|
-
Sets the stock level at which this variant is considered to be out
|
|
288
|
-
of stock. Using a negative value enables backorder support.
|
|
289
|
-
</Trans>
|
|
290
|
-
</FormDescription>
|
|
291
|
-
</FormItem>
|
|
273
|
+
<Switch checked={field.value} onCheckedChange={field.onChange} />
|
|
292
274
|
)}
|
|
293
275
|
/>
|
|
294
276
|
</DetailFormGrid>
|