mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +00:00
feat(webapp): import preview page
This commit is contained in:
@@ -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;
|
||||
|
||||
248
packages/webapp/src/components/Section/Section.tsx
Normal file
248
packages/webapp/src/components/Section/Section.tsx
Normal 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`;
|
||||
41
packages/webapp/src/components/Section/SectionCard.tsx
Normal file
41
packages/webapp/src/components/Section/SectionCard.tsx
Normal 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`;
|
||||
2
packages/webapp/src/components/Section/index.ts
Normal file
2
packages/webapp/src/components/Section/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './Section';
|
||||
export * from './SectionCard';
|
||||
@@ -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 };
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 32px 20px;
|
||||
padding-bottom: 80px;
|
||||
min-width: 660px;
|
||||
max-width: 760px;
|
||||
width: 75%;
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
// fonts
|
||||
@import 'pages/fonts';
|
||||
@import "section";
|
||||
|
||||
.App {
|
||||
min-width: 960px;
|
||||
|
||||
@@ -9,7 +9,7 @@ body{
|
||||
}
|
||||
|
||||
.#{$ns}-heading{
|
||||
font-weight: 400;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.divider{
|
||||
|
||||
117
packages/webapp/src/style/section.scss
Normal file
117
packages/webapp/src/style/section.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user