feat(webapp): import preview page

This commit is contained in:
Ahmed Bouhuolia
2024-03-22 00:05:10 +02:00
parent 1d8cec5069
commit a5ab535d3b
13 changed files with 619 additions and 48 deletions

View File

@@ -17,11 +17,20 @@
import classNames from 'classnames';
import * as React from 'react';
import { Classes } from '@blueprintjs/core';
import { Classes, Props } from '@blueprintjs/core';
import IconSvgPaths from '@/static/json/icons';
import PropTypes from 'prop-types';
export interface IconProps extends Props {
color?: string;
htmlTitle?: string;
icon: IconName | MaybeElement;
iconSize?: number;
style?: object;
tagName?: keyof JSX.IntrinsicElements;
title?: string;
}
export class Icon extends React.Component {
export class Icon extends React.Component<IconProps> {
static displayName = `af.Icon`;
static SIZE_STANDARD = 16;

View File

@@ -0,0 +1,248 @@
import classNames from 'classnames';
import React from 'react';
import {
Card,
Collapse,
type CollapseProps,
Elevation,
Utils,
DISPLAYNAME_PREFIX,
type HTMLDivProps,
type MaybeElement,
type Props,
IconName,
} from '@blueprintjs/core';
import { H6 } from '@blueprintjs/core';
import { CLASSES } from '@/constants';
import { Icon } from '../Icon';
/**
* Subset of {@link Elevation} options which are visually supported by the {@link Section} component.
*
* Note that an elevation greater than 1 creates too much visual clutter/noise in the UI, especially when
* multiple Sections are shown on a single page.
*/
export type SectionElevation = typeof Elevation.ZERO | typeof Elevation.ONE;
export interface SectionCollapseProps
extends Pick<
CollapseProps,
'className' | 'isOpen' | 'keepChildrenMounted' | 'transitionDuration'
> {
/**
* Whether the component is initially open or closed.
*
* This prop has no effect if `collapsible={false}` or the component is in controlled mode,
* i.e. when `isOpen` is **not** `undefined`.
*
* @default true
*/
defaultIsOpen?: boolean;
/**
* Whether the component is open or closed.
*
* Passing a boolean value to `isOpen` will enabled controlled mode for the component.
*/
isOpen?: boolean;
/**
* Callback invoked in controlled mode when the collapse toggle element is clicked.
*/
onToggle?: () => void;
}
export interface SectionProps extends Props, Omit<HTMLDivProps, 'title'> {
/**
* Whether this section's contents should be collapsible.
*
* @default false
*/
collapsible?: boolean;
/**
* Subset of props to forward to the underlying {@link Collapse} component, with the addition of a
* `defaultIsOpen` option which sets the default open state of the component when in uncontrolled mode.
*/
collapseProps?: SectionCollapseProps;
/**
* Whether this section should use compact styles.
*
* @default false
*/
compact?: boolean;
/**
* Visual elevation of this container element.
*
* @default Elevation.ZERO
*/
elevation?: SectionElevation;
/**
* Name of a Blueprint UI icon (or an icon element) to render in the section's header.
* Note that the header will only be rendered if `title` is provided.
*/
icon?: IconName | MaybeElement;
/**
* Element to render on the right side of the section header.
* Note that the header will only be rendered if `title` is provided.
*/
rightElement?: JSX.Element;
/**
* Sub-title of the section.
* Note that the header will only be rendered if `title` is provided.
*/
subtitle?: JSX.Element | string;
/**
* Title of the section.
* Note that the header will only be rendered if `title` is provided.
*/
title?: JSX.Element | string;
/**
* Optional title renderer function. If provided, it is recommended to include a Blueprint `<H6>` element
* as part of the title. The render function is supplied with `className` and `id` attributes which you must
* forward to the DOM. The `title` prop is also passed along to this renderer via `props.children`.
*
* @default H6
*/
titleRenderer?: React.FC<React.HTMLAttributes<HTMLElement>>;
}
/**
* Section component.
*
* @see https://blueprintjs.com/docs/#core/components/section
*/
export const Section: React.FC<SectionProps> = React.forwardRef(
(props, ref) => {
const {
children,
className,
collapseProps,
collapsible,
compact,
elevation,
icon,
rightElement,
subtitle,
title,
titleRenderer = H6,
...htmlProps
} = props;
// Determine whether to use controlled or uncontrolled state.
const isControlled = collapseProps?.isOpen != null;
// The initial useState value is negated in order to conform to the `isCollapsed` expectation.
const [isCollapsedUncontrolled, setIsCollapsed] = React.useState<boolean>(
!(collapseProps?.defaultIsOpen ?? true),
);
const isCollapsed = isControlled
? !collapseProps?.isOpen
: isCollapsedUncontrolled;
const toggleIsCollapsed = React.useCallback(() => {
if (isControlled) {
collapseProps?.onToggle?.();
} else {
setIsCollapsed(!isCollapsed);
}
}, [collapseProps, isCollapsed, isControlled]);
const isHeaderRightContainerVisible = rightElement != null || collapsible;
const sectionId = Utils.uniqueId('section');
const sectionTitleId = title ? Utils.uniqueId('section-title') : undefined;
return (
<Card
className={classNames(className, CLASSES.SECTION, {
[CLASSES.COMPACT]: compact,
[CLASSES.SECTION_COLLAPSED]:
(collapsible && isCollapsed) || Utils.isReactNodeEmpty(children),
})}
elevation={elevation}
aria-labelledby={sectionTitleId}
{...htmlProps}
id={sectionId}
>
{title && (
<div
role={collapsible ? 'button' : undefined}
aria-pressed={collapsible ? isCollapsed : undefined}
aria-expanded={collapsible ? isCollapsed : undefined}
aria-controls={collapsible ? sectionId : undefined}
className={classNames(CLASSES.SECTION_HEADER, {
[CLASSES.INTERACTIVE]: collapsible,
})}
onClick={collapsible ? toggleIsCollapsed : undefined}
>
<div className={CLASSES.SECTION_HEADER_LEFT}>
{/* {icon && (
<Icon
icon={icon}
aria-hidden={true}
tabIndex={-1}
className={CLASSES.TEXT_MUTED}
/>
)} */}
<div>
{React.createElement(
titleRenderer,
{
className: CLASSES.SECTION_HEADER_TITLE,
id: sectionTitleId,
},
title,
)}
{subtitle && (
<div
className={classNames(
CLASSES.TEXT_MUTED,
CLASSES.SECTION_HEADER_SUB_TITLE,
)}
>
{subtitle}
</div>
)}
</div>
</div>
{isHeaderRightContainerVisible && (
<div className={CLASSES.SECTION_HEADER_RIGHT}>
{rightElement}
{collapsible &&
(isCollapsed ? (
<Icon
icon={'chevron-down'}
className={CLASSES.TEXT_MUTED}
/>
) : (
<Icon icon={'chevron-up'} className={CLASSES.TEXT_MUTED} />
))}
</div>
)}
</div>
)}
{collapsible ? (
// @ts-ignore
<Collapse isOpen={!isCollapsed}>
{children}
</Collapse>
) : (
children
)}
</Card>
);
},
);
Section.defaultProps = {
compact: false,
elevation: Elevation.ZERO,
};
Section.displayName = `${DISPLAYNAME_PREFIX}.Section`;

View File

@@ -0,0 +1,41 @@
import classNames from 'classnames';
import * as React from 'react';
import { DISPLAYNAME_PREFIX, HTMLDivProps, Props } from '@blueprintjs/core';
import { CLASSES } from '@/constants';
export interface SectionCardProps
extends Props,
HTMLDivProps,
React.RefAttributes<HTMLDivElement> {
/**
* Whether to apply visual padding inside the content container element.
*
* @default true
*/
padded?: boolean;
}
/**
* Section card component.
*
* @see https://blueprintjs.com/docs/#core/components/section.section-card
*/
export const SectionCard: React.FC<SectionCardProps> = React.forwardRef(
(props, ref) => {
const { className, children, padded, ...htmlProps } = props;
const classes = classNames(
CLASSES.SECTION_CARD,
{ [CLASSES.PADDED]: padded },
className,
);
return (
<div className={classes} ref={ref} {...htmlProps}>
{children}
</div>
);
},
);
SectionCard.defaultProps = {
padded: true,
};
SectionCard.displayName = `${DISPLAYNAME_PREFIX}.SectionCard`;

View File

@@ -0,0 +1,2 @@
export * from './Section';
export * from './SectionCard';

View File

@@ -1,6 +1,21 @@
// @ts-nocheck
import { Classes } from '@blueprintjs/core';
export const NS = 'bp4';
export const SECTION = `${NS}-section`;
export const SECTION_COLLAPSED = `${SECTION}-collapsed`;
export const SECTION_HEADER = `${SECTION}-header`;
export const SECTION_HEADER_LEFT = `${SECTION_HEADER}-left`;
export const SECTION_HEADER_TITLE = `${SECTION_HEADER}-title`;
export const SECTION_HEADER_SUB_TITLE = `${SECTION_HEADER}-sub-title`;
export const SECTION_HEADER_DIVIDER = `${SECTION_HEADER}-divider`;
export const SECTION_HEADER_TABS = `${SECTION_HEADER}-tabs`;
export const SECTION_HEADER_RIGHT = `${SECTION_HEADER}-right`;
export const SECTION_CARD = `${SECTION}-card`;
export const PADDED = `${NS}-padded`;
const CLASSES = {
DASHBOARD_PAGE: 'dashboard__page',
DASHBOARD_DATATABLE: 'dashboard__datatable',
@@ -16,7 +31,7 @@ const CLASSES = {
DASHBOARD_CONTENT_PREFERENCES: 'dashboard-content--preferences',
DASHBOARD_CONTENT_PANE: 'Pane2',
DASHBOARD_CENTERED_EMPTY_STATUS: 'dashboard__centered-empty-status',
PAGE_FORM: 'page-form',
PAGE_FORM_HEADER: 'page-form__header',
PAGE_FORM_HEADER_PRIMARY: 'page-form__primary-section',
@@ -40,9 +55,9 @@ const CLASSES = {
PAGE_FORM_ITEM: 'page-form--item',
PAGE_FORM_MAKE_JOURNAL: 'page-form--make-journal-entries',
PAGE_FORM_EXPENSE: 'page-form--expense',
PAGE_FORM_CREDIT_NOTE:'page-form--credit-note',
PAGE_FORM_VENDOR_CREDIT_NOTE:'page-form--vendor-credit-note',
PAGE_FORM_WAREHOUSE_TRANSFER:'page-form--warehouse-transfer',
PAGE_FORM_CREDIT_NOTE: 'page-form--credit-note',
PAGE_FORM_VENDOR_CREDIT_NOTE: 'page-form--vendor-credit-note',
PAGE_FORM_WAREHOUSE_TRANSFER: 'page-form--warehouse-transfer',
FORM_GROUP_LIST_SELECT: 'form-group--select-list',
@@ -66,31 +81,42 @@ const CLASSES = {
PREFERENCES_TOPBAR: 'preferences-topbar',
PREFERENCES_PAGE_INSIDE_CONTENT: 'preferences-page__inside-content',
PREFERENCES_PAGE_INSIDE_CONTENT_GENERAL: 'preferences-page__inside-content--general',
PREFERENCES_PAGE_INSIDE_CONTENT_USERS: 'preferences-page__inside-content--users',
PREFERENCES_PAGE_INSIDE_CONTENT_CURRENCIES: 'preferences-page__inside-content--currencies',
PREFERENCES_PAGE_INSIDE_CONTENT_ACCOUNTANT: 'preferences-page__inside-content--accountant',
PREFERENCES_PAGE_INSIDE_CONTENT_SMS_INTEGRATION: 'preferences-page__inside-content--sms-integration',
PREFERENCES_PAGE_INSIDE_CONTENT_ROLES_FORM: 'preferences-page__inside-content--roles-form',
PREFERENCES_PAGE_INSIDE_CONTENT_BRANCHES: 'preferences-page__inside-content--branches',
PREFERENCES_PAGE_INSIDE_CONTENT_WAREHOUSES: 'preferences-page__inside-content--warehouses',
PREFERENCES_PAGE_INSIDE_CONTENT_GENERAL:
'preferences-page__inside-content--general',
PREFERENCES_PAGE_INSIDE_CONTENT_USERS:
'preferences-page__inside-content--users',
PREFERENCES_PAGE_INSIDE_CONTENT_CURRENCIES:
'preferences-page__inside-content--currencies',
PREFERENCES_PAGE_INSIDE_CONTENT_ACCOUNTANT:
'preferences-page__inside-content--accountant',
PREFERENCES_PAGE_INSIDE_CONTENT_SMS_INTEGRATION:
'preferences-page__inside-content--sms-integration',
PREFERENCES_PAGE_INSIDE_CONTENT_ROLES_FORM:
'preferences-page__inside-content--roles-form',
PREFERENCES_PAGE_INSIDE_CONTENT_BRANCHES:
'preferences-page__inside-content--branches',
PREFERENCES_PAGE_INSIDE_CONTENT_WAREHOUSES:
'preferences-page__inside-content--warehouses',
FINANCIAL_REPORT_INSIDER: 'dashboard__insider--financial-report',
UNIVERSAL_SEARCH: 'universal-search',
UNIVERSAL_SEARCH_OMNIBAR: 'universal-search__omnibar',
UNIVERSAL_SEARCH_OVERLAY: 'universal-search-overlay',
UNIVERSAL_SEARCH_INPUT: 'universal-search__input',
UNIVERSAL_SEARCH_INPUT_RIGHT_ELEMENTS: 'universal-search-input-right-elements',
UNIVERSAL_SEARCH_INPUT_RIGHT_ELEMENTS:
'universal-search-input-right-elements',
UNIVERSAL_SEARCH_TYPE_SELECT_OVERLAY: 'universal-search__type-select-overlay',
UNIVERSAL_SEARCH_TYPE_SELECT_BTN: 'universal-search__type-select-btn',
UNIVERSAL_SEARCH_FOOTER: 'universal-search__footer',
UNIVERSAL_SEARCH_ACTIONS: 'universal-search__actions',
UNIVERSAL_SEARCH_ACTION_SELECT: 'universal-search__action universal-search__action--select',
UNIVERSAL_SEARCH_ACTION_CLOSE: 'universal-search__action universal-search__action--close',
UNIVERSAL_SEARCH_ACTION_ARROWS: 'universal-search__action universal-search__action--arrows',
UNIVERSAL_SEARCH_ACTION_SELECT:
'universal-search__action universal-search__action--select',
UNIVERSAL_SEARCH_ACTION_CLOSE:
'universal-search__action universal-search__action--close',
UNIVERSAL_SEARCH_ACTION_ARROWS:
'universal-search__action universal-search__action--arrows',
DIALOG_PDF_PREVIEW: 'dialog--pdf-preview-dialog',
@@ -98,8 +124,19 @@ const CLASSES = {
CARD: 'card',
ALIGN_RIGHT: 'align-right',
FONT_BOLD: 'font-bold',
NS,
PADDED,
SECTION,
SECTION_COLLAPSED,
SECTION_HEADER,
SECTION_HEADER_LEFT,
SECTION_HEADER_TITLE,
SECTION_HEADER_SUB_TITLE,
SECTION_HEADER_DIVIDER,
SECTION_HEADER_TABS,
SECTION_HEADER_RIGHT,
SECTION_CARD,
};
export {
CLASSES,
}
export { CLASSES };

View File

@@ -1,11 +1,11 @@
// @ts-nocheck
import { Intent } from '@blueprintjs/core';
import { useImportFileMapping } from '@/hooks/query/import';
import { Form, Formik, FormikHelpers } from 'formik';
import { useImportFileContext } from './ImportFileProvider';
import { useMemo } from 'react';
import { isEmpty } from 'lodash';
const validationSchema = null;
import { AppToaster } from '@/components';
interface ImportFileMappingFormProps {
children: React.ReactNode;
@@ -33,7 +33,13 @@ export function ImportFileMappingForm({
setSubmitting(false);
setStep(2);
})
.catch((error) => {
.catch(({ response: { data } }) => {
if (data.errors.find(e => e.type === "DUPLICATED_FROM_MAP_ATTR")) {
AppToaster.show({
message: 'Selected the same sheet columns to multiple fields.',
intent: Intent.DANGER
})
}
setSubmitting(false);
});
};
@@ -42,7 +48,6 @@ export function ImportFileMappingForm({
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
// validationSchema={validationSchema}
>
<Form>{children}</Form>
</Formik>

View File

@@ -0,0 +1,42 @@
.previewList {
list-style: none;
margin-top: 14px;
:global(li) {
border-top: 1px solid #d9d9da;
padding: 6px 0;
&:last-child{
padding-bottom: 0;
}
}
}
.unmappedList {
padding-left: 2rem;
}
.skippedTable {
width: 100%;
thead{
th{
padding-top: 0;
padding-bottom: 8px;
color: #738091;
font-weight: 500;
}
}
tbody{
tr td {
vertical-align: middle;
padding: 7px;
}
tr:hover td{
background: #F6F7F9;
}
}
}

View File

@@ -7,10 +7,13 @@ import {
useImportFilePreviewBootContext,
} from './ImportFilePreviewBoot';
import { useImportFileContext } from './ImportFileProvider';
import { AppToaster, Card, Group } from '@/components';
import { useImportFileProcess } from '@/hooks/query/import';
import { AppToaster, Box, Group, Stack } from '@/components';
import { CLASSES } from '@/constants';
import { ImportStepperStep } from './_types';
import { ImportFileContainer } from './ImportFileContainer';
import { SectionCard, Section } from '@/components/Section';
import styles from './ImportFilePreview.module.scss';
export function ImportFilePreview() {
const { importId } = useImportFileContext();
@@ -26,23 +29,68 @@ function ImportFilePreviewContent() {
const { importPreview } = useImportFilePreviewBootContext();
return (
<div>
<Callout>
{importPreview.createdCount} of {importPreview.totalCount} Items in your
file are ready to be imported.
</Callout>
<Box>
<ImportFileContainer>
<Stack spacing={20}>
<Callout
intent={
importPreview.createdCount <= 0 ? Intent.DANGER : Intent.NONE
}
>
{importPreview.createdCount} of {importPreview.totalCount} Items in
your file are ready to be imported.
</Callout>
<Card>
<ImportFilePreviewImported />
<ImportFilePreviewSkipped />
<ImportFilePreviewUnmapped />
</Stack>
</ImportFileContainer>
<ImportFilePreviewFloatingActions />
</Box>
);
}
function ImportFilePreviewImported() {
const { importPreview } = useImportFilePreviewBootContext();
return (
<Section
collapseProps={{ defaultIsOpen: false }}
defaultIsOpen={true}
title={`(${importPreview.createdCount}) Items are ready to import`}
>
<SectionCard padded={true}>
<Text>
Items that are ready to be imported - {importPreview.createdCount}
</Text>
<ul>
<li>Items to be created: ({importPreview.createdCount})</li>
<li>Items to be skipped: ({importPreview.skippedCount})</li>
<li>Items have errors: ({importPreview.errorsCount})</li>
<ul className={styles.previewList}>
<li>
Items to be created: <span>({importPreview.createdCount})</span>
</li>
<li>
Items to be skipped: <span>({importPreview.skippedCount})</span>
</li>
<li>
Items have errors: <span>({importPreview.errorsCount})</span>
</li>
</ul>
</SectionCard>
</Section>
);
}
<table className={clsx('bp4-html-table')}>
function ImportFilePreviewSkipped() {
const { importPreview } = useImportFilePreviewBootContext();
return (
<Section
collapseProps={{ defaultIsOpen: false }}
collapsible={true}
title={`(${importPreview.skippedCount}) Items are skipped`}
>
<SectionCard padded={true}>
<table className={clsx('bp4-html-table', styles.skippedTable)}>
<thead>
<tr>
<th className={'number'}>#</th>
@@ -64,20 +112,28 @@ function ImportFilePreviewContent() {
))}
</tbody>
</table>
</SectionCard>
</Section>
);
}
<Text>
Unmapped Sheet Columns - ({importPreview?.unmappedColumnsCount})
</Text>
function ImportFilePreviewUnmapped() {
const { importPreview } = useImportFilePreviewBootContext();
<ul>
return (
<Section
collapseProps={{ defaultIsOpen: false }}
collapsible={true}
title={`(${importPreview?.unmappedColumnsCount}) Unmapped Columns`}
>
<SectionCard padded={true}>
<ul className={styles.unmappedList}>
{importPreview.unmappedColumns?.map((column, key) => (
<li key={key}>{column}</li>
))}
</ul>
</Card>
<ImportFilePreviewFloatingActions />
</div>
</SectionCard>
</Section>
);
}

View File

@@ -5,6 +5,7 @@
.content {
flex: 1;
padding: 32px 20px;
padding-bottom: 80px;
min-width: 660px;
max-width: 760px;
width: 75%;

View File

@@ -588,5 +588,17 @@ export default {
'M480-336 288-528l51-51 105 105v-342h72v342l105-105 51 51-192 192ZM263.717-192Q234-192 213-213.15T192-264v-72h72v72h432v-72h72v72q0 29.7-21.162 50.85Q725.676-192 695.96-192H263.717Z'
],
viewBox: '0 -960 960 960'
}
},
'chevron-up': {
path: [
'M12.71,9.29l-4-4C8.53,5.11,8.28,5,8,5S7.47,5.11,7.29,5.29l-4,4C3.11,9.47,3,9.72,3,10c0,0.55,0.45,1,1,1c0.28,0,0.53-0.11,0.71-0.29L8,7.41l3.29,3.29C11.47,10.89,11.72,11,12,11c0.55,0,1-0.45,1-1C13,9.72,12.89,9.47,12.71,9.29'
],
viewBox: '0 0 16 16',
},
'chevron-down': {
path: [
'M12,5c-0.28,0-0.53,0.11-0.71,0.29L8,8.59L4.71,5.29C4.53,5.11,4.28,5,4,5C3.45,5,3,5.45,3,6c0,0.28,0.11,0.53,0.29,0.71l4,4C7.47,10.89,7.72,11,8,11s0.53-0.11,0.71-0.29l4-4C12.89,6.53,13,6.28,13,6C13,5.45,12.55,5,12,5z'
],
viewBox: '0 0 16 16',
},
};

View File

@@ -36,6 +36,7 @@
// fonts
@import 'pages/fonts';
@import "section";
.App {
min-width: 960px;

View File

@@ -9,7 +9,7 @@ body{
}
.#{$ns}-heading{
font-weight: 400;
font-weight: 600;
}
.divider{

View File

@@ -0,0 +1,117 @@
@use "sass:math";
@import './_base.scss';
$section-min-height: $pt-grid-size * 5 !default;
$section-padding-vertical: $pt-grid-size !default;
$section-padding-horizontal: $pt-grid-size * 2 !default;
$section-card-padding: $pt-grid-size * 2 !default;
$section-min-height-compact: $pt-grid-size * 4 !default;
$section-padding-compact-vertical: 7px !default;
$section-padding-compact-horizontal: 15px !default;
$section-card-padding-compact: $pt-grid-size * 1.5 !default;
.#{$ns}-section {
overflow: hidden;
width: 100%;
&,
&.#{$ns}-compact {
// override Card compact styles here
padding: 0;
}
&-header {
align-items: center;
border-bottom: 1px solid $pt-divider-black;
display: flex;
gap: $pt-grid-size * 2;
justify-content: space-between;
min-height: $section-min-height;
padding: 0 $section-padding-horizontal;
position: relative;
width: 100%;
&.#{$ns}-dark,
.#{$ns}-dark & {
border-color: $pt-dark-divider-white;
}
&-left {
align-items: center;
display: flex;
gap: $pt-grid-size;
padding: $section-padding-vertical 0;
}
&-title {
margin-bottom: 0;
}
&-sub-title {
margin-top: 2px;
}
&-right {
align-items: center;
display: flex;
gap: $pt-grid-size;
margin-left: auto;
}
&-divider {
align-self: stretch;
margin: $pt-grid-size * 1.5 0;
}
&.#{$ns}-interactive {
cursor: pointer;
&:hover,
&:active {
background: $light-gray5;
&.#{$ns}-dark,
.#{$ns}-dark & {
background: $dark-gray4;
}
}
}
}
&-card {
&.#{$ns}-padded {
padding: $section-card-padding;
}
&:not(:last-child) {
border-bottom: 1px solid $pt-divider-black;
&.#{$ns}-dark,
.#{$ns}-dark & {
border-color: $pt-dark-divider-white;
}
}
}
&.#{$ns}-section-collapsed {
.#{$ns}-section-header {
border: none;
}
}
&.#{$ns}-compact {
.#{$ns}-section-header {
min-height: $section-min-height-compact;
padding: 0 $section-padding-compact-horizontal;
&-left {
padding: $section-padding-compact-vertical 0;
}
}
.#{$ns}-section-card.#{$ns}-padded {
padding: $section-card-padding-compact;
}
}
}