Fix attribute errors
This commit is contained in:
parent
d63d9b41be
commit
4d27960d79
14 changed files with 153 additions and 114 deletions
|
@ -1,6 +1,6 @@
|
|||
schema {
|
||||
query: Query
|
||||
mutation: Mutations
|
||||
mutation: Mutation
|
||||
}
|
||||
|
||||
type AccountAddressCreate {
|
||||
|
@ -405,7 +405,6 @@ type AttributeValueDelete {
|
|||
|
||||
input AttributeValueInput {
|
||||
id: ID
|
||||
slug: String
|
||||
values: [String]!
|
||||
}
|
||||
|
||||
|
@ -1990,7 +1989,7 @@ input MoveProductInput {
|
|||
sortOrder: Int
|
||||
}
|
||||
|
||||
type Mutations {
|
||||
type Mutation {
|
||||
webhookCreate(input: WebhookCreateInput!): WebhookCreate
|
||||
webhookDelete(id: ID!): WebhookDelete
|
||||
webhookUpdate(id: ID!, input: WebhookUpdateInput!): WebhookUpdate
|
||||
|
@ -3383,6 +3382,8 @@ type Query {
|
|||
serviceAccount(id: ID!): ServiceAccount
|
||||
user(id: ID!): User
|
||||
node(id: ID!): Node
|
||||
_entities(representations: [_Any]): [_Entity]
|
||||
_service: _Service
|
||||
}
|
||||
|
||||
type ReducedRate {
|
||||
|
@ -4324,6 +4325,7 @@ enum WebhookEventTypeEnum {
|
|||
ORDER_FULLY_PAID
|
||||
ORDER_UPDATED
|
||||
ORDER_CANCELLED
|
||||
ORDER_FULFILLED
|
||||
CUSTOMER_CREATED
|
||||
PRODUCT_CREATED
|
||||
}
|
||||
|
@ -4361,3 +4363,11 @@ enum WeightUnitsEnum {
|
|||
OZ
|
||||
G
|
||||
}
|
||||
|
||||
scalar _Any
|
||||
|
||||
union _Entity = Address | ServiceAccount | User | ProductVariant | Product | ProductType | Collection | Category | ProductImage
|
||||
|
||||
type _Service {
|
||||
sdl: String
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import Card from "@material-ui/core/Card";
|
||||
import CardContent from "@material-ui/core/CardContent";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { IntlShape, useIntl } from "react-intl";
|
||||
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import FormSpacer from "@saleor/components/FormSpacer";
|
||||
import Grid from "@saleor/components/Grid";
|
||||
import SingleAutocompleteSelectField, {
|
||||
SingleAutocompleteChoiceType
|
||||
|
@ -11,6 +13,8 @@ import SingleAutocompleteSelectField, {
|
|||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import { FormsetAtomicData, FormsetChange } from "@saleor/hooks/useFormset";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { VariantCreate_productVariantCreate_productErrors } from "@saleor/products/types/VariantCreate";
|
||||
import { ProductErrorCode } from "@saleor/types/globalTypes";
|
||||
import { ProductVariant_attributes_attribute_values } from "../../types/ProductVariant";
|
||||
|
||||
export interface VariantAttributeInputData {
|
||||
|
@ -24,7 +28,7 @@ export type VariantAttributeInput = FormsetAtomicData<
|
|||
interface ProductVariantAttributesProps {
|
||||
attributes: VariantAttributeInput[];
|
||||
disabled: boolean;
|
||||
errors: Record<string, string>;
|
||||
errors: VariantCreate_productVariantCreate_productErrors[];
|
||||
onChange: FormsetChange<VariantAttributeInputData>;
|
||||
}
|
||||
|
||||
|
@ -63,6 +67,19 @@ function getAttributeValueChoices(
|
|||
}));
|
||||
}
|
||||
|
||||
function translateErrors(intl: IntlShape) {
|
||||
return {
|
||||
[ProductErrorCode.REQUIRED]: intl.formatMessage({
|
||||
defaultMessage: "All attributes should have value",
|
||||
description: "product attribute error"
|
||||
}),
|
||||
[ProductErrorCode.UNIQUE]: intl.formatMessage({
|
||||
defaultMessage: "This variant already exists",
|
||||
description: "product attribute error"
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
const ProductVariantAttributes: React.FC<ProductVariantAttributesProps> = ({
|
||||
attributes,
|
||||
disabled,
|
||||
|
@ -71,6 +88,8 @@ const ProductVariantAttributes: React.FC<ProductVariantAttributesProps> = ({
|
|||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const translatedErrors = translateErrors(intl);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
|
@ -81,30 +100,38 @@ const ProductVariantAttributes: React.FC<ProductVariantAttributesProps> = ({
|
|||
{attributes === undefined ? (
|
||||
<Skeleton />
|
||||
) : (
|
||||
attributes.map((attribute, attributeIndex) => {
|
||||
return (
|
||||
<SingleAutocompleteSelectField
|
||||
key={attributeIndex}
|
||||
disabled={disabled}
|
||||
displayValue={getAttributeDisplayValue(
|
||||
attribute.id,
|
||||
attribute.value,
|
||||
attributes
|
||||
)}
|
||||
error={!!errors[attribute.id]}
|
||||
helperText={errors[attribute.id]}
|
||||
label={attribute.label}
|
||||
name={`attribute:${attribute.id}`}
|
||||
onChange={event => onChange(attribute.id, event.target.value)}
|
||||
value={getAttributeValue(attribute.id, attributes)}
|
||||
choices={getAttributeValueChoices(attribute.id, attributes)}
|
||||
allowCustomValues
|
||||
data-tc="variant-attribute-input"
|
||||
/>
|
||||
);
|
||||
})
|
||||
attributes.map(attribute => (
|
||||
<SingleAutocompleteSelectField
|
||||
key={attribute.id}
|
||||
disabled={disabled}
|
||||
displayValue={getAttributeDisplayValue(
|
||||
attribute.id,
|
||||
attribute.value,
|
||||
attributes
|
||||
)}
|
||||
label={attribute.label}
|
||||
name={`attribute:${attribute.id}`}
|
||||
onChange={event => onChange(attribute.id, event.target.value)}
|
||||
value={getAttributeValue(attribute.id, attributes)}
|
||||
choices={getAttributeValueChoices(attribute.id, attributes)}
|
||||
allowCustomValues
|
||||
data-tc="variant-attribute-input"
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</Grid>
|
||||
{errors.length > 0 && (
|
||||
<>
|
||||
<FormSpacer />
|
||||
{errors
|
||||
.filter(error => error.field === "attributes")
|
||||
.map(error => (
|
||||
<Typography color="error" key={error.code}>
|
||||
{translatedErrors[error.code]}
|
||||
</Typography>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
|
|
@ -13,12 +13,9 @@ import useFormset, {
|
|||
FormsetChange,
|
||||
FormsetData
|
||||
} from "@saleor/hooks/useFormset";
|
||||
import {
|
||||
getVariantAttributeErrors,
|
||||
getVariantAttributeInputFromProduct
|
||||
} from "@saleor/products/utils/data";
|
||||
import { VariantCreate_productVariantCreate_productErrors } from "@saleor/products/types/VariantCreate";
|
||||
import { getVariantAttributeInputFromProduct } from "@saleor/products/utils/data";
|
||||
import { maybe } from "../../../misc";
|
||||
import { UserError } from "../../../types";
|
||||
import { ProductVariantCreateData_product } from "../../types/ProductVariantCreateData";
|
||||
import ProductVariantAttributes, {
|
||||
VariantAttributeInputData
|
||||
|
@ -42,7 +39,7 @@ export interface ProductVariantCreatePageSubmitData
|
|||
|
||||
interface ProductVariantCreatePageProps {
|
||||
currencySymbol: string;
|
||||
errors: UserError[];
|
||||
errors: VariantCreate_productVariantCreate_productErrors[];
|
||||
header: string;
|
||||
loading: boolean;
|
||||
product: ProductVariantCreateData_product;
|
||||
|
@ -54,7 +51,7 @@ interface ProductVariantCreatePageProps {
|
|||
|
||||
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
||||
currencySymbol,
|
||||
errors: formErrors,
|
||||
errors: apiErrors,
|
||||
loading,
|
||||
header,
|
||||
product,
|
||||
|
@ -96,7 +93,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
|||
});
|
||||
|
||||
return (
|
||||
<Form initial={initialForm} errors={formErrors} onSubmit={handleSubmit}>
|
||||
<Form initial={initialForm} errors={apiErrors} onSubmit={handleSubmit}>
|
||||
{({ change, data, errors, hasChanged, submit, triggerChange }) => {
|
||||
const handleAttributeChange: FormsetChange = (id, value) => {
|
||||
changeAttributeData(id, value);
|
||||
|
@ -123,10 +120,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = ({
|
|||
<ProductVariantAttributes
|
||||
attributes={attributes}
|
||||
disabled={loading}
|
||||
errors={getVariantAttributeErrors(
|
||||
formErrors,
|
||||
maybe(() => product.productType.variantAttributes)
|
||||
)}
|
||||
errors={apiErrors}
|
||||
onChange={handleAttributeChange}
|
||||
/>
|
||||
<CardSpacer />
|
||||
|
|
|
@ -12,12 +12,9 @@ import useFormset, {
|
|||
FormsetChange,
|
||||
FormsetData
|
||||
} from "@saleor/hooks/useFormset";
|
||||
import {
|
||||
getAttributeInputFromVariant,
|
||||
getVariantAttributeErrors
|
||||
} from "@saleor/products/utils/data";
|
||||
import { VariantUpdate_productVariantUpdate_productErrors } from "@saleor/products/types/VariantUpdate";
|
||||
import { getAttributeInputFromVariant } from "@saleor/products/utils/data";
|
||||
import { maybe } from "../../../misc";
|
||||
import { UserError } from "../../../types";
|
||||
import { ProductVariant } from "../../types/ProductVariant";
|
||||
import ProductVariantAttributes, {
|
||||
VariantAttributeInputData
|
||||
|
@ -42,7 +39,7 @@ export interface ProductVariantPageSubmitData
|
|||
|
||||
interface ProductVariantPageProps {
|
||||
variant?: ProductVariant;
|
||||
errors: UserError[];
|
||||
errors: VariantUpdate_productVariantUpdate_productErrors[];
|
||||
saveButtonBarState: ConfirmButtonTransitionState;
|
||||
loading?: boolean;
|
||||
placeholderImage?: string;
|
||||
|
@ -56,7 +53,7 @@ interface ProductVariantPageProps {
|
|||
}
|
||||
|
||||
const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
||||
errors: formErrors,
|
||||
errors: apiErrors,
|
||||
loading,
|
||||
header,
|
||||
placeholderImage,
|
||||
|
@ -114,7 +111,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
<PageHeader title={header} />
|
||||
<Form
|
||||
initial={initialForm}
|
||||
errors={formErrors}
|
||||
errors={apiErrors}
|
||||
onSubmit={handleSubmit}
|
||||
confirmLeave
|
||||
>
|
||||
|
@ -146,14 +143,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
<ProductVariantAttributes
|
||||
attributes={attributes}
|
||||
disabled={loading}
|
||||
errors={getVariantAttributeErrors(
|
||||
formErrors,
|
||||
maybe(() =>
|
||||
variant.attributes.map(
|
||||
attribute => attribute.attribute
|
||||
)
|
||||
)
|
||||
)}
|
||||
errors={apiErrors}
|
||||
onChange={handleAttributeChange}
|
||||
/>
|
||||
<CardSpacer />
|
||||
|
|
|
@ -306,7 +306,8 @@ export const variantUpdateMutation = gql`
|
|||
trackInventory: $trackInventory
|
||||
}
|
||||
) {
|
||||
errors {
|
||||
productErrors {
|
||||
code
|
||||
field
|
||||
message
|
||||
}
|
||||
|
@ -325,7 +326,8 @@ export const variantCreateMutation = gql`
|
|||
${fragmentVariant}
|
||||
mutation VariantCreate($input: ProductVariantCreateInput!) {
|
||||
productVariantCreate(input: $input) {
|
||||
errors {
|
||||
productErrors {
|
||||
code
|
||||
field
|
||||
message
|
||||
}
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { ProductVariantCreateInput } from "./../../types/globalTypes";
|
||||
import { ProductVariantCreateInput, ProductErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: VariantCreate
|
||||
// ====================================================
|
||||
|
||||
export interface VariantCreate_productVariantCreate_errors {
|
||||
__typename: "Error";
|
||||
export interface VariantCreate_productVariantCreate_productErrors {
|
||||
__typename: "ProductError";
|
||||
code: ProductErrorCode | null;
|
||||
field: string | null;
|
||||
message: string | null;
|
||||
}
|
||||
|
@ -113,7 +114,7 @@ export interface VariantCreate_productVariantCreate_productVariant {
|
|||
|
||||
export interface VariantCreate_productVariantCreate {
|
||||
__typename: "ProductVariantCreate";
|
||||
errors: VariantCreate_productVariantCreate_errors[] | null;
|
||||
productErrors: VariantCreate_productVariantCreate_productErrors[] | null;
|
||||
productVariant: VariantCreate_productVariantCreate_productVariant | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeValueInput } from "./../../types/globalTypes";
|
||||
import { AttributeValueInput, ProductErrorCode } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL mutation operation: VariantUpdate
|
||||
// ====================================================
|
||||
|
||||
export interface VariantUpdate_productVariantUpdate_errors {
|
||||
__typename: "Error";
|
||||
export interface VariantUpdate_productVariantUpdate_productErrors {
|
||||
__typename: "ProductError";
|
||||
code: ProductErrorCode | null;
|
||||
field: string | null;
|
||||
message: string | null;
|
||||
}
|
||||
|
@ -113,7 +114,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant {
|
|||
|
||||
export interface VariantUpdate_productVariantUpdate {
|
||||
__typename: "ProductVariantUpdate";
|
||||
errors: VariantUpdate_productVariantUpdate_errors[] | null;
|
||||
productErrors: VariantUpdate_productVariantUpdate_productErrors[] | null;
|
||||
productVariant: VariantUpdate_productVariantUpdate_productVariant | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,17 +9,10 @@ import {
|
|||
ProductDetails_product_collections,
|
||||
ProductDetails_product_variants
|
||||
} from "@saleor/products/types/ProductDetails";
|
||||
import { UserError } from "@saleor/types";
|
||||
import { ProductAttributeInput } from "../components/ProductAttributes";
|
||||
import { VariantAttributeInput } from "../components/ProductVariantAttributes";
|
||||
import {
|
||||
ProductVariant,
|
||||
ProductVariant_attributes_attribute
|
||||
} from "../types/ProductVariant";
|
||||
import {
|
||||
ProductVariantCreateData_product,
|
||||
ProductVariantCreateData_product_productType_variantAttributes
|
||||
} from "../types/ProductVariantCreateData";
|
||||
import { ProductVariant } from "../types/ProductVariant";
|
||||
import { ProductVariantCreateData_product } from "../types/ProductVariantCreateData";
|
||||
|
||||
export interface Collection {
|
||||
id: string;
|
||||
|
@ -197,28 +190,3 @@ export function getProductUpdatePageFormData(
|
|||
)
|
||||
};
|
||||
}
|
||||
|
||||
export function getVariantAttributeErrors(
|
||||
errors: UserError[],
|
||||
variantAttributes: Array<
|
||||
| ProductVariantCreateData_product_productType_variantAttributes
|
||||
| ProductVariant_attributes_attribute
|
||||
>
|
||||
): Record<string, string> {
|
||||
return maybe(
|
||||
() =>
|
||||
errors.reduce((acc, err) => {
|
||||
const slug = err.field.split(":")[1];
|
||||
const attribute = variantAttributes.find(
|
||||
attribute => attribute.slug === slug
|
||||
);
|
||||
|
||||
if (!!attribute) {
|
||||
acc[attribute.id] = err.message;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {}),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
|||
navigate(productUrl(productId));
|
||||
};
|
||||
const handleUpdate = (data: VariantUpdate) => {
|
||||
if (!maybe(() => data.productVariantUpdate.errors.length)) {
|
||||
if (!maybe(() => data.productVariantUpdate.productErrors.length)) {
|
||||
notify({ text: intl.formatMessage(commonMessages.savedChanges) });
|
||||
}
|
||||
};
|
||||
|
@ -74,7 +74,10 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
|||
const formTransitionState = getMutationState(
|
||||
updateVariant.opts.called,
|
||||
updateVariant.opts.loading,
|
||||
maybe(() => updateVariant.opts.data.productVariantUpdate.errors)
|
||||
maybe(
|
||||
() =>
|
||||
updateVariant.opts.data.productVariantUpdate.productErrors
|
||||
)
|
||||
);
|
||||
const removeTransitionState = getMutationState(
|
||||
deleteVariant.opts.called,
|
||||
|
@ -105,7 +108,9 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
|||
<WindowTitle title={maybe(() => data.productVariant.name)} />
|
||||
<ProductVariantPage
|
||||
errors={maybe(
|
||||
() => updateVariant.opts.data.productVariantUpdate.errors,
|
||||
() =>
|
||||
updateVariant.opts.data.productVariantUpdate
|
||||
.productErrors,
|
||||
[]
|
||||
)}
|
||||
saveButtonBarState={formTransitionState}
|
||||
|
|
|
@ -34,7 +34,7 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
|||
>
|
||||
{({ data, loading: productLoading }) => {
|
||||
const handleCreateSuccess = (data: VariantCreate) => {
|
||||
if (data.productVariantCreate.errors.length === 0) {
|
||||
if (data.productVariantCreate.productErrors.length === 0) {
|
||||
notify({
|
||||
text: intl.formatMessage({
|
||||
defaultMessage: "Product created"
|
||||
|
@ -83,7 +83,8 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
|||
variantCreateResult.called,
|
||||
variantCreateResult.loading,
|
||||
maybe(
|
||||
() => variantCreateResult.data.productVariantCreate.errors
|
||||
() =>
|
||||
variantCreateResult.data.productVariantCreate.productErrors
|
||||
)
|
||||
);
|
||||
return (
|
||||
|
@ -98,7 +99,8 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
|||
currencySymbol={maybe(() => shop.defaultCurrency)}
|
||||
errors={maybe(
|
||||
() =>
|
||||
variantCreateResult.data.productVariantCreate.errors,
|
||||
variantCreateResult.data.productVariantCreate
|
||||
.productErrors,
|
||||
[]
|
||||
)}
|
||||
header={intl.formatMessage({
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
export const formError = (field: string) => ({
|
||||
field,
|
||||
message: "Generic form error"
|
||||
});
|
||||
export function formError<TErrorPayload>(
|
||||
field: string,
|
||||
opts?: Partial<Omit<TErrorPayload, "field" | "message">>
|
||||
) {
|
||||
return {
|
||||
field,
|
||||
message: "Generic form error",
|
||||
...opts
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { storiesOf } from "@storybook/react";
|
|||
import React from "react";
|
||||
|
||||
import placeholderImage from "@assets/images/placeholder255x255.png";
|
||||
import { formError } from "@saleor/storybook/misc";
|
||||
import { ProductErrorCode } from "@saleor/types/globalTypes";
|
||||
import ProductVariantCreatePage from "../../../products/components/ProductVariantCreatePage";
|
||||
import { product as productFixture } from "../../../products/fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
|
@ -27,7 +27,24 @@ storiesOf("Views / Products / Create product variant", module)
|
|||
.add("with errors", () => (
|
||||
<ProductVariantCreatePage
|
||||
currencySymbol="USD"
|
||||
errors={[formError("attributes:color")]}
|
||||
errors={[
|
||||
{
|
||||
code: ProductErrorCode.REQUIRED,
|
||||
field: "attributes"
|
||||
},
|
||||
{
|
||||
code: ProductErrorCode.UNIQUE,
|
||||
field: "attributes"
|
||||
},
|
||||
{
|
||||
code: ProductErrorCode.ALREADY_EXISTS,
|
||||
field: "sku"
|
||||
}
|
||||
].map(error => ({
|
||||
__typename: "ProductError",
|
||||
message: "Generic form error",
|
||||
...error
|
||||
}))}
|
||||
header="Add variant"
|
||||
loading={false}
|
||||
product={product}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { storiesOf } from "@storybook/react";
|
|||
import React from "react";
|
||||
|
||||
import placeholderImage from "@assets/images/placeholder60x60.png";
|
||||
import { formError } from "@saleor/storybook/misc";
|
||||
import { ProductErrorCode } from "@saleor/types/globalTypes";
|
||||
import ProductVariantPage from "../../../products/components/ProductVariantPage";
|
||||
import { variant as variantFixture } from "../../../products/fixtures";
|
||||
import Decorator from "../../Decorator";
|
||||
|
@ -51,6 +51,23 @@ storiesOf("Views / Products / Product variant details", module)
|
|||
onSubmit={() => undefined}
|
||||
onVariantClick={() => undefined}
|
||||
saveButtonBarState="default"
|
||||
errors={["attributes:Borders", "attributes:Legacy"].map(formError)}
|
||||
errors={[
|
||||
{
|
||||
code: ProductErrorCode.REQUIRED,
|
||||
field: "attributes"
|
||||
},
|
||||
{
|
||||
code: ProductErrorCode.UNIQUE,
|
||||
field: "attributes"
|
||||
},
|
||||
{
|
||||
code: ProductErrorCode.ALREADY_EXISTS,
|
||||
field: "sku"
|
||||
}
|
||||
].map(error => ({
|
||||
__typename: "ProductError",
|
||||
message: "Generic form error",
|
||||
...error
|
||||
}))}
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -382,7 +382,6 @@ export interface AttributeValueCreateInput {
|
|||
|
||||
export interface AttributeValueInput {
|
||||
id?: string | null;
|
||||
slug?: string | null;
|
||||
values: (string | null)[];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue