Product variants enhancements (#1918)
* Remove savebar docking * Hide attributes if empty * Select text in autocomplete after initial click * Update snapshots * Use stable macaw version * Remove maybes * Add util filter function
This commit is contained in:
parent
3e96a68a6e
commit
ac4a219023
6 changed files with 191 additions and 291 deletions
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -4828,8 +4828,9 @@
|
|||
}
|
||||
},
|
||||
"@saleor/macaw-ui": {
|
||||
"version": "github:saleor/macaw-ui#1f78f97748c00a64ca46973c32eacc4d9a1ac2ac",
|
||||
"from": "github:saleor/macaw-ui#SALEOR-5840-button-states",
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.3.3.tgz",
|
||||
"integrity": "sha512-b2dLlOAXDe2OSmYeZSFKwiRMrJc+YkGhvvQCHQXHHP89TZbGZlWP0F13ycCI3OJSfYbCWZlQGM+alMiE31HOuA==",
|
||||
"requires": {
|
||||
"clsx": "^1.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.58",
|
||||
"@material-ui/styles": "^4.11.4",
|
||||
"@saleor/macaw-ui": "github:saleor/macaw-ui#SALEOR-5840-button-states",
|
||||
"@saleor/macaw-ui": "^0.3.3",
|
||||
"@saleor/sdk": "^0.4.2",
|
||||
"@sentry/react": "^6.0.0",
|
||||
"@types/faker": "^5.1.6",
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
} from "@saleor/macaw-ui";
|
||||
import { isDarkTheme } from "@saleor/misc";
|
||||
import { staffMemberDetailsUrl } from "@saleor/staff/urls";
|
||||
import classNames from "classnames";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import useRouter from "use-react-router";
|
||||
|
@ -41,9 +40,6 @@ const useStyles = makeStyles(
|
|||
position: "sticky",
|
||||
zIndex: 10
|
||||
},
|
||||
appActionDocked: {
|
||||
position: "static"
|
||||
},
|
||||
appLoader: {
|
||||
height: appLoaderHeight,
|
||||
marginBottom: theme.spacing(4),
|
||||
|
@ -123,7 +119,7 @@ interface AppLayoutProps {
|
|||
const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||
const classes = useStyles({});
|
||||
const { themeType, setTheme } = useTheme();
|
||||
const { anchor: appActionAnchor, docked } = useActionBar();
|
||||
const { anchor: appActionAnchor } = useActionBar();
|
||||
const appHeaderAnchor = useBacklink();
|
||||
const { logout, user } = useUser();
|
||||
const navigate = useNavigator();
|
||||
|
@ -234,12 +230,7 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
|||
: children}
|
||||
</main>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(classes.appAction, {
|
||||
[classes.appActionDocked]: docked
|
||||
})}
|
||||
ref={appActionAnchor}
|
||||
/>
|
||||
<div className={classes.appAction} ref={appActionAnchor} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -90,7 +90,8 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
|
|||
...rest
|
||||
} = props;
|
||||
const classes = useStyles(props);
|
||||
const anchor = React.useRef<HTMLInputElement | null>(null);
|
||||
const anchor = React.useRef<HTMLDivElement | null>(null);
|
||||
const input = React.useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const handleChange = (item: string) => {
|
||||
onChange({
|
||||
|
@ -188,6 +189,7 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
|
|||
if (fetchOnFocus) {
|
||||
fetchChoices(inputValue);
|
||||
}
|
||||
input.current.select();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -224,6 +226,7 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
|
|||
fullWidth={true}
|
||||
onBlur={onBlur}
|
||||
ref={anchor}
|
||||
inputRef={input}
|
||||
/>
|
||||
{isOpen && (!!inputValue || !!choices.length) && (
|
||||
<Popper
|
||||
|
|
|
@ -29,7 +29,6 @@ import { FetchMoreProps, RelayToFlat, ReorderAction } from "@saleor/types";
|
|||
import React from "react";
|
||||
import { defineMessages, useIntl } from "react-intl";
|
||||
|
||||
import { maybe } from "../../../misc";
|
||||
import ProductShipping from "../ProductShipping/ProductShipping";
|
||||
import ProductStocks, { ProductStockInput } from "../ProductStocks";
|
||||
import ProductVariantCheckoutSettings from "../ProductVariantCheckoutSettings/ProductVariantCheckoutSettings";
|
||||
|
@ -73,6 +72,11 @@ export interface ProductVariantPageSubmitData
|
|||
removeStocks: string[];
|
||||
}
|
||||
|
||||
function byAttributeScope(scope: VariantAttributeScope) {
|
||||
return (attribute: AttributeInput) =>
|
||||
attribute.data.variantAttributeScope === scope;
|
||||
}
|
||||
|
||||
interface ProductVariantPageProps {
|
||||
assignReferencesAttributeId?: string;
|
||||
defaultVariantId?: string;
|
||||
|
@ -223,175 +227,184 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
handlers,
|
||||
hasChanged,
|
||||
submit
|
||||
}) => (
|
||||
<>
|
||||
<Grid variant="inverted">
|
||||
<div>
|
||||
<ProductVariantNavigation
|
||||
current={variant ? variant.id : undefined}
|
||||
defaultVariantId={defaultVariantId}
|
||||
fallbackThumbnail={maybe(
|
||||
() => variant.product.thumbnail.url
|
||||
)}
|
||||
variants={maybe(() => variant.product.variants)}
|
||||
onAdd={onAdd}
|
||||
onRowClick={(variantId: string) => {
|
||||
if (variant) {
|
||||
return onVariantClick(variantId);
|
||||
}
|
||||
}}
|
||||
onReorder={onVariantReorder}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<VariantDetailsChannelsAvailabilityCard variant={variant} />
|
||||
<Attributes
|
||||
entityId={variant?.id}
|
||||
title={intl.formatMessage(messages.nonSelectionAttributes)}
|
||||
attributes={data.attributes.filter(
|
||||
attribute =>
|
||||
attribute.data.variantAttributeScope ===
|
||||
VariantAttributeScope.NOT_VARIANT_SELECTION
|
||||
)}
|
||||
attributeValues={attributeValues}
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
errors={errors}
|
||||
onChange={handlers.selectAttribute}
|
||||
onMultiChange={handlers.selectAttributeMultiple}
|
||||
onFileChange={handlers.selectAttributeFile}
|
||||
onReferencesRemove={handlers.selectAttributeReference}
|
||||
onReferencesAddClick={onAssignReferencesClick}
|
||||
onReferencesReorder={handlers.reorderAttributeValue}
|
||||
fetchAttributeValues={fetchAttributeValues}
|
||||
fetchMoreAttributeValues={fetchMoreAttributeValues}
|
||||
onAttributeSelectBlur={onAttributeSelectBlur}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Attributes
|
||||
entityId={variant?.id}
|
||||
title={intl.formatMessage(
|
||||
messages.selectionAttributesHeader
|
||||
)}
|
||||
attributes={data.attributes.filter(
|
||||
attribute =>
|
||||
attribute.data.variantAttributeScope ===
|
||||
VariantAttributeScope.VARIANT_SELECTION
|
||||
)}
|
||||
attributeValues={attributeValues}
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
errors={errors}
|
||||
onChange={handlers.selectAttribute}
|
||||
onMultiChange={handlers.selectAttributeMultiple}
|
||||
onFileChange={handlers.selectAttributeFile}
|
||||
onReferencesRemove={handlers.selectAttributeReference}
|
||||
onReferencesAddClick={onAssignReferencesClick}
|
||||
onReferencesReorder={handlers.reorderAttributeValue}
|
||||
fetchAttributeValues={fetchAttributeValues}
|
||||
fetchMoreAttributeValues={fetchMoreAttributeValues}
|
||||
onAttributeSelectBlur={onAttributeSelectBlur}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ProductVariantMedia
|
||||
disabled={loading}
|
||||
media={media}
|
||||
placeholderImage={placeholderImage}
|
||||
onImageAdd={toggleModal}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ProductVariantPrice
|
||||
disabled={!variant}
|
||||
ProductVariantChannelListings={data.channelListings.map(
|
||||
channel => ({
|
||||
...channel.data,
|
||||
...channel.value
|
||||
})
|
||||
)}
|
||||
errors={channelErrors}
|
||||
loading={loading}
|
||||
onChange={handlers.changeChannels}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ProductVariantCheckoutSettings
|
||||
data={data}
|
||||
disabled={loading}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
}) => {
|
||||
const nonSelectionAttributes = data.attributes.filter(
|
||||
byAttributeScope(VariantAttributeScope.NOT_VARIANT_SELECTION)
|
||||
);
|
||||
const selectionAttributes = data.attributes.filter(
|
||||
byAttributeScope(VariantAttributeScope.VARIANT_SELECTION)
|
||||
);
|
||||
|
||||
<ProductShipping
|
||||
data={data}
|
||||
disabled={loading}
|
||||
errors={errors}
|
||||
weightUnit={variant?.weight?.unit || defaultWeightUnit}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ProductStocks
|
||||
productVariantChannelListings={data.channelListings.map(
|
||||
channel => ({
|
||||
...channel.data,
|
||||
...channel.value
|
||||
})
|
||||
return (
|
||||
<>
|
||||
<Grid variant="inverted">
|
||||
<div>
|
||||
<ProductVariantNavigation
|
||||
current={variant?.id}
|
||||
defaultVariantId={defaultVariantId}
|
||||
fallbackThumbnail={variant?.product.thumbnail.url}
|
||||
variants={variant?.product.variants}
|
||||
onAdd={onAdd}
|
||||
onRowClick={(variantId: string) => {
|
||||
if (variant) {
|
||||
return onVariantClick(variantId);
|
||||
}
|
||||
}}
|
||||
onReorder={onVariantReorder}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<VariantDetailsChannelsAvailabilityCard variant={variant} />
|
||||
{nonSelectionAttributes.length > 0 && (
|
||||
<>
|
||||
<Attributes
|
||||
entityId={variant?.id}
|
||||
title={intl.formatMessage(
|
||||
messages.nonSelectionAttributes
|
||||
)}
|
||||
attributes={nonSelectionAttributes}
|
||||
attributeValues={attributeValues}
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
errors={errors}
|
||||
onChange={handlers.selectAttribute}
|
||||
onMultiChange={handlers.selectAttributeMultiple}
|
||||
onFileChange={handlers.selectAttributeFile}
|
||||
onReferencesRemove={handlers.selectAttributeReference}
|
||||
onReferencesAddClick={onAssignReferencesClick}
|
||||
onReferencesReorder={handlers.reorderAttributeValue}
|
||||
fetchAttributeValues={fetchAttributeValues}
|
||||
fetchMoreAttributeValues={fetchMoreAttributeValues}
|
||||
onAttributeSelectBlur={onAttributeSelectBlur}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</>
|
||||
)}
|
||||
onVariantChannelListingChange={handlers.changeChannels}
|
||||
data={data}
|
||||
disabled={loading}
|
||||
hasVariants={true}
|
||||
errors={errors}
|
||||
formErrors={formErrors}
|
||||
stocks={data.stocks}
|
||||
warehouses={warehouses}
|
||||
onChange={handlers.changeStock}
|
||||
onFormDataChange={change}
|
||||
onChangePreorderEndDate={handlers.changePreorderEndDate}
|
||||
onEndPreorderTrigger={
|
||||
!!variant?.preorder
|
||||
? () => setIsEndPreorderModalOpened(true)
|
||||
: null
|
||||
}
|
||||
onWarehouseStockAdd={handlers.addStock}
|
||||
onWarehouseStockDelete={handlers.deleteStock}
|
||||
onWarehouseConfigure={onWarehouseConfigure}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={loading || formDisabled || !hasChanged}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
/>
|
||||
{canOpenAssignReferencesAttributeDialog && (
|
||||
<AssignAttributeValueDialog
|
||||
attributeValues={getAttributeValuesFromReferences(
|
||||
assignReferencesAttributeId,
|
||||
data.attributes,
|
||||
referencePages,
|
||||
referenceProducts
|
||||
)}
|
||||
hasMore={handlers.fetchMoreReferences?.hasMore}
|
||||
open={canOpenAssignReferencesAttributeDialog}
|
||||
onFetch={handlers.fetchReferences}
|
||||
onFetchMore={handlers.fetchMoreReferences?.onFetchMore}
|
||||
loading={handlers.fetchMoreReferences?.loading}
|
||||
onClose={onCloseDialog}
|
||||
onSubmit={attributeValues =>
|
||||
handleAssignReferenceAttribute(
|
||||
attributeValues,
|
||||
data,
|
||||
handlers
|
||||
)
|
||||
}
|
||||
{selectionAttributes.length > 0 && (
|
||||
<>
|
||||
<Attributes
|
||||
entityId={variant?.id}
|
||||
title={intl.formatMessage(
|
||||
messages.selectionAttributesHeader
|
||||
)}
|
||||
attributes={selectionAttributes}
|
||||
attributeValues={attributeValues}
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
errors={errors}
|
||||
onChange={handlers.selectAttribute}
|
||||
onMultiChange={handlers.selectAttributeMultiple}
|
||||
onFileChange={handlers.selectAttributeFile}
|
||||
onReferencesRemove={handlers.selectAttributeReference}
|
||||
onReferencesAddClick={onAssignReferencesClick}
|
||||
onReferencesReorder={handlers.reorderAttributeValue}
|
||||
fetchAttributeValues={fetchAttributeValues}
|
||||
fetchMoreAttributeValues={fetchMoreAttributeValues}
|
||||
onAttributeSelectBlur={onAttributeSelectBlur}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</>
|
||||
)}
|
||||
<ProductVariantMedia
|
||||
disabled={loading}
|
||||
media={media}
|
||||
placeholderImage={placeholderImage}
|
||||
onImageAdd={toggleModal}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ProductVariantPrice
|
||||
disabled={!variant}
|
||||
ProductVariantChannelListings={data.channelListings.map(
|
||||
channel => ({
|
||||
...channel.data,
|
||||
...channel.value
|
||||
})
|
||||
)}
|
||||
errors={channelErrors}
|
||||
loading={loading}
|
||||
onChange={handlers.changeChannels}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ProductVariantCheckoutSettings
|
||||
data={data}
|
||||
disabled={loading}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
|
||||
<ProductShipping
|
||||
data={data}
|
||||
disabled={loading}
|
||||
errors={errors}
|
||||
weightUnit={variant?.weight?.unit || defaultWeightUnit}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ProductStocks
|
||||
productVariantChannelListings={data.channelListings.map(
|
||||
channel => ({
|
||||
...channel.data,
|
||||
...channel.value
|
||||
})
|
||||
)}
|
||||
onVariantChannelListingChange={handlers.changeChannels}
|
||||
data={data}
|
||||
disabled={loading}
|
||||
hasVariants={true}
|
||||
errors={errors}
|
||||
formErrors={formErrors}
|
||||
stocks={data.stocks}
|
||||
warehouses={warehouses}
|
||||
onChange={handlers.changeStock}
|
||||
onFormDataChange={change}
|
||||
onChangePreorderEndDate={handlers.changePreorderEndDate}
|
||||
onEndPreorderTrigger={
|
||||
!!variant?.preorder
|
||||
? () => setIsEndPreorderModalOpened(true)
|
||||
: null
|
||||
}
|
||||
onWarehouseStockAdd={handlers.addStock}
|
||||
onWarehouseStockDelete={handlers.deleteStock}
|
||||
onWarehouseConfigure={onWarehouseConfigure}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
</div>
|
||||
</Grid>
|
||||
<Savebar
|
||||
disabled={loading || formDisabled || !hasChanged}
|
||||
state={saveButtonBarState}
|
||||
onCancel={onBack}
|
||||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{canOpenAssignReferencesAttributeDialog && (
|
||||
<AssignAttributeValueDialog
|
||||
attributeValues={getAttributeValuesFromReferences(
|
||||
assignReferencesAttributeId,
|
||||
data.attributes,
|
||||
referencePages,
|
||||
referenceProducts
|
||||
)}
|
||||
hasMore={handlers.fetchMoreReferences?.hasMore}
|
||||
open={canOpenAssignReferencesAttributeDialog}
|
||||
onFetch={handlers.fetchReferences}
|
||||
onFetchMore={handlers.fetchMoreReferences?.onFetchMore}
|
||||
loading={handlers.fetchMoreReferences?.loading}
|
||||
onClose={onCloseDialog}
|
||||
onSubmit={attributeValues =>
|
||||
handleAssignReferenceAttribute(
|
||||
attributeValues,
|
||||
data,
|
||||
handlers
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</ProductVariantUpdateForm>
|
||||
</Container>
|
||||
{variant && (
|
||||
|
@ -400,7 +413,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
|
|||
onMediaSelect={onMediaSelect}
|
||||
open={isModalOpened}
|
||||
media={productMedia}
|
||||
selectedMedia={maybe(() => variant.media.map(image => image.id))}
|
||||
selectedMedia={variant?.media.map(image => image.id)}
|
||||
/>
|
||||
)}
|
||||
{!!variant?.preorder && (
|
||||
|
|
|
@ -225170,114 +225170,6 @@ exports[`Storyshots Views / Products / Product variant details when loading data
|
|||
<div
|
||||
class="CardSpacer-spacer-id"
|
||||
/>
|
||||
<div
|
||||
class="MuiPaper-root-id MuiCard-root-id Attributes-card-id MuiPaper-elevation0-id MuiPaper-rounded-id"
|
||||
>
|
||||
<div
|
||||
class="MuiCardHeader-root-id"
|
||||
>
|
||||
<div
|
||||
class="MuiCardHeader-content-id"
|
||||
>
|
||||
<span
|
||||
class="MuiTypography-root-id MuiCardHeader-title-id MuiTypography-h5-id MuiTypography-displayBlock-id"
|
||||
>
|
||||
Variant Attributes
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiCardContent-root-id Attributes-cardContent-id"
|
||||
>
|
||||
<div
|
||||
class="Attributes-expansionBar-id"
|
||||
>
|
||||
<div
|
||||
class="Attributes-expansionBarLabelContainer-id"
|
||||
>
|
||||
<div
|
||||
class="MuiTypography-root-id Attributes-expansionBarLabel-id MuiTypography-caption-id"
|
||||
>
|
||||
0 Attributes
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="MuiButtonBase-root-id IconButton-secondary-id Attributes-expansionBarButton-id IconButton-hoverOutline-id"
|
||||
data-test-id="attributes-expand"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root-id Attributes-expansionBarButtonIcon-id Attributes-rotate-id"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M7 10l5 5 5-5z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="CardSpacer-spacer-id"
|
||||
/>
|
||||
<div
|
||||
class="MuiPaper-root-id MuiCard-root-id Attributes-card-id MuiPaper-elevation0-id MuiPaper-rounded-id"
|
||||
>
|
||||
<div
|
||||
class="MuiCardHeader-root-id"
|
||||
>
|
||||
<div
|
||||
class="MuiCardHeader-content-id"
|
||||
>
|
||||
<span
|
||||
class="MuiTypography-root-id MuiCardHeader-title-id MuiTypography-h5-id MuiTypography-displayBlock-id"
|
||||
>
|
||||
Variant Selection Attributes
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiCardContent-root-id Attributes-cardContent-id"
|
||||
>
|
||||
<div
|
||||
class="Attributes-expansionBar-id"
|
||||
>
|
||||
<div
|
||||
class="Attributes-expansionBarLabelContainer-id"
|
||||
>
|
||||
<div
|
||||
class="MuiTypography-root-id Attributes-expansionBarLabel-id MuiTypography-caption-id"
|
||||
>
|
||||
0 Attributes
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="MuiButtonBase-root-id IconButton-secondary-id Attributes-expansionBarButton-id IconButton-hoverOutline-id"
|
||||
data-test-id="attributes-expand"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root-id Attributes-expansionBarButtonIcon-id Attributes-rotate-id"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M7 10l5 5 5-5z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="CardSpacer-spacer-id"
|
||||
/>
|
||||
<div
|
||||
class="MuiPaper-root-id MuiCard-root-id MuiPaper-elevation0-id MuiPaper-rounded-id"
|
||||
>
|
||||
|
|
Loading…
Reference in a new issue