@servicetitan/mpa-components 1.10.0 → 2.0.0
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/CHANGELOG.md +16 -0
- package/lib/components/brands/brand-card/actions-button-section.d.ts +6 -0
- package/lib/components/brands/brand-card/actions-button-section.d.ts.map +1 -0
- package/lib/components/brands/brand-card/actions-button-section.js +26 -0
- package/lib/components/brands/brand-card/actions-button-section.js.map +1 -0
- package/lib/components/brands/brand-card/brand-card.d.ts +4 -0
- package/lib/components/brands/brand-card/brand-card.d.ts.map +1 -0
- package/lib/components/brands/brand-card/brand-card.js +16 -0
- package/lib/components/brands/brand-card/brand-card.js.map +1 -0
- package/lib/components/brands/brand-card/brand-logo.d.ts +6 -0
- package/lib/components/brands/brand-card/brand-logo.d.ts.map +1 -0
- package/lib/components/brands/brand-card/brand-logo.js +8 -0
- package/lib/components/brands/brand-card/brand-logo.js.map +1 -0
- package/lib/components/brands/brand-card/cart-tags.d.ts +6 -0
- package/lib/components/brands/brand-card/cart-tags.d.ts.map +1 -0
- package/lib/components/brands/brand-card/cart-tags.js +10 -0
- package/lib/components/brands/brand-card/cart-tags.js.map +1 -0
- package/lib/components/brands/brand-card/name-and-mail-section.d.ts +7 -0
- package/lib/components/brands/brand-card/name-and-mail-section.d.ts.map +1 -0
- package/lib/components/brands/brand-card/name-and-mail-section.js +7 -0
- package/lib/components/brands/brand-card/name-and-mail-section.js.map +1 -0
- package/lib/components/brands/brand-card/single-action-button.d.ts +6 -0
- package/lib/components/brands/brand-card/single-action-button.d.ts.map +1 -0
- package/lib/components/brands/brand-card/single-action-button.js +34 -0
- package/lib/components/brands/brand-card/single-action-button.js.map +1 -0
- package/lib/components/brands/cards-grid/cards-grid.d.ts +7 -0
- package/lib/components/brands/cards-grid/cards-grid.d.ts.map +1 -0
- package/lib/components/brands/cards-grid/cards-grid.js +7 -0
- package/lib/components/brands/cards-grid/cards-grid.js.map +1 -0
- package/lib/components/brands/index.d.ts +2 -0
- package/lib/components/brands/index.d.ts.map +1 -0
- package/lib/components/brands/index.js +2 -0
- package/lib/components/brands/index.js.map +1 -0
- package/lib/components/brands/styles.module.less +59 -0
- package/lib/enums/brands.d.ts +6 -0
- package/lib/enums/brands.d.ts.map +1 -0
- package/lib/enums/brands.js +7 -0
- package/lib/enums/brands.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/utils/interfaces.d.ts +22 -0
- package/lib/utils/interfaces.d.ts.map +1 -0
- package/lib/utils/interfaces.js +2 -0
- package/lib/utils/interfaces.js.map +1 -0
- package/lib/utils/mappers.d.ts +4 -0
- package/lib/utils/mappers.d.ts.map +1 -0
- package/lib/utils/mappers.js +13 -0
- package/lib/utils/mappers.js.map +1 -0
- package/package.json +15 -11
- package/src/components/brands/brand-card/actions-button-section.tsx +62 -0
- package/src/components/brands/brand-card/brand-card.tsx +55 -0
- package/src/components/brands/brand-card/brand-logo.tsx +21 -0
- package/src/components/brands/brand-card/cart-tags.tsx +20 -0
- package/src/components/brands/brand-card/name-and-mail-section.tsx +28 -0
- package/src/components/brands/brand-card/single-action-button.tsx +84 -0
- package/src/components/brands/cards-grid/cards-grid.tsx +20 -0
- package/src/components/brands/index.ts +1 -0
- package/src/components/brands/styles.module.less +59 -0
- package/src/components/brands/styles.module.less.d.ts +10 -0
- package/src/enums/brands.ts +5 -0
- package/src/index.ts +3 -1
- package/src/utils/interfaces.ts +23 -0
- package/src/utils/mappers.ts +14 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
|
|
4
|
+
import { Flex, Card } from '@servicetitan/anvil2';
|
|
5
|
+
|
|
6
|
+
import { NameAndMailSection } from './name-and-mail-section';
|
|
7
|
+
import { BrandCardProps } from '../../../utils/interfaces';
|
|
8
|
+
import { ActionsButtonSection } from './actions-button-section';
|
|
9
|
+
import { CardTags } from './cart-tags';
|
|
10
|
+
import { BrandLogo } from './brand-logo';
|
|
11
|
+
|
|
12
|
+
import * as Styles from '../styles.module.less';
|
|
13
|
+
|
|
14
|
+
const BRAND_NAME_PLACEHOLDER = 'Assign Brand Name';
|
|
15
|
+
|
|
16
|
+
export const BrandCard: FC<BrandCardProps> = ({
|
|
17
|
+
actions,
|
|
18
|
+
email,
|
|
19
|
+
isDefault,
|
|
20
|
+
isShared,
|
|
21
|
+
logo,
|
|
22
|
+
name,
|
|
23
|
+
hasError,
|
|
24
|
+
}) => {
|
|
25
|
+
const nameToShow = name || BRAND_NAME_PLACEHOLDER;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Card
|
|
29
|
+
className={classNames(
|
|
30
|
+
{
|
|
31
|
+
[Styles.cardWithError]: hasError,
|
|
32
|
+
},
|
|
33
|
+
Styles.brandCard,
|
|
34
|
+
'qa-brand-card',
|
|
35
|
+
)}
|
|
36
|
+
padding="large"
|
|
37
|
+
>
|
|
38
|
+
<Flex className={Styles.cardContent} justifyContent="space-between" direction="column">
|
|
39
|
+
<Flex
|
|
40
|
+
justifyContent="space-between"
|
|
41
|
+
alignItems="flex-start"
|
|
42
|
+
className="qa-brand-card-left-section"
|
|
43
|
+
>
|
|
44
|
+
<BrandLogo logo={logo} alt={nameToShow} />
|
|
45
|
+
<ActionsButtonSection actions={actions} />
|
|
46
|
+
</Flex>
|
|
47
|
+
|
|
48
|
+
<Flex justifyContent="space-between">
|
|
49
|
+
<NameAndMailSection name={name} nameToShow={nameToShow} email={email} />
|
|
50
|
+
<CardTags isDefault={isDefault} isShared={isShared} />
|
|
51
|
+
</Flex>
|
|
52
|
+
</Flex>
|
|
53
|
+
</Card>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
|
|
4
|
+
import { Flex, Icon } from '@servicetitan/anvil2';
|
|
5
|
+
import ImageIcon from '@servicetitan/anvil2/assets/icons/material/round/image.svg';
|
|
6
|
+
import { tokens } from '@servicetitan/tokens/core/index';
|
|
7
|
+
|
|
8
|
+
import * as Styles from '../styles.module.less';
|
|
9
|
+
|
|
10
|
+
export const BrandLogo: FC<{ logo?: string; alt?: string }> = ({ logo, alt }) =>
|
|
11
|
+
logo ? (
|
|
12
|
+
<img className={classNames(Styles.brandImg, 'qa-brand-logo')} src={logo} alt={alt} />
|
|
13
|
+
) : (
|
|
14
|
+
<Flex
|
|
15
|
+
className={classNames(Styles.brandImgPlaceholder, 'qa-brand-logo-placeholder')}
|
|
16
|
+
alignItems="center"
|
|
17
|
+
justifyContent="center"
|
|
18
|
+
>
|
|
19
|
+
<Icon svg={ImageIcon} size="large" color={tokens.colorNeutral90} />
|
|
20
|
+
</Flex>
|
|
21
|
+
);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Chip, Flex } from '@servicetitan/anvil2';
|
|
4
|
+
import { tokens } from '@servicetitan/tokens/core/index';
|
|
5
|
+
|
|
6
|
+
export const CardTags: FC<{ isDefault?: boolean; isShared?: boolean }> = ({
|
|
7
|
+
isDefault,
|
|
8
|
+
isShared,
|
|
9
|
+
}) => {
|
|
10
|
+
if (!isDefault && !isShared) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Flex direction="column" alignItems="flex-end" className="qa-brand-card-tags" gap="1">
|
|
16
|
+
{isShared && <Chip color={tokens.colorPurple100} label="Corporate HQ" size="small" />}
|
|
17
|
+
{isDefault && <Chip label="Default" color={tokens.colorNeutral50} size="small" />}
|
|
18
|
+
</Flex>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
|
|
4
|
+
import { BodyText, Headline } from '@servicetitan/design-system';
|
|
5
|
+
import { Flex } from '@servicetitan/anvil2';
|
|
6
|
+
|
|
7
|
+
import * as Styles from '../styles.module.less';
|
|
8
|
+
|
|
9
|
+
export const NameAndMailSection: FC<{ name?: string; nameToShow: string; email?: string }> = ({
|
|
10
|
+
name,
|
|
11
|
+
email,
|
|
12
|
+
nameToShow,
|
|
13
|
+
}) => (
|
|
14
|
+
<Flex
|
|
15
|
+
justifyContent="space-between"
|
|
16
|
+
direction="column"
|
|
17
|
+
className={classNames(Styles.cardLeftSectionTexts, 'qa-brand-card-name-and-mail')}
|
|
18
|
+
>
|
|
19
|
+
<Headline el="p" subdued={!name} className="t-truncate-i m-0" title={nameToShow}>
|
|
20
|
+
{nameToShow}
|
|
21
|
+
</Headline>
|
|
22
|
+
{email && (
|
|
23
|
+
<BodyText subdued title={email} className="t-truncate-i">
|
|
24
|
+
{email}
|
|
25
|
+
</BodyText>
|
|
26
|
+
)}
|
|
27
|
+
</Flex>
|
|
28
|
+
);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { FC, PropsWithChildren } from 'react';
|
|
2
|
+
import { useHistory } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import HelpIcon from '@servicetitan/anvil2/assets/icons/material/round/help.svg';
|
|
5
|
+
import { Chip, Flex, Tooltip as TooltipA2, Button } from '@servicetitan/anvil2';
|
|
6
|
+
import { ButtonAppearance } from '@servicetitan/hammer-react/dist/types/props';
|
|
7
|
+
|
|
8
|
+
import { BrandAction } from '../../../utils/interfaces';
|
|
9
|
+
import { BrandActionChipColor, BrandActionChipLabel } from '../../../utils/mappers';
|
|
10
|
+
import { ActionStatus } from '../../../enums/brands';
|
|
11
|
+
|
|
12
|
+
export const SingleActionButtonSection: FC<{
|
|
13
|
+
action: BrandAction;
|
|
14
|
+
}> = ({ action }) => {
|
|
15
|
+
const history = useHistory();
|
|
16
|
+
const handleClick = () => {
|
|
17
|
+
if (action.action) {
|
|
18
|
+
action.action();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (action.href) {
|
|
22
|
+
history.push(action.href);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
let buttonAppearance: ButtonAppearance | undefined;
|
|
27
|
+
if (action.status === undefined) {
|
|
28
|
+
buttonAppearance = 'primary';
|
|
29
|
+
} else if (!action.disabled) {
|
|
30
|
+
buttonAppearance = 'secondary';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Flex gap="2" alignItems="center" className="qa-brand-card-single-action">
|
|
35
|
+
<ActionChip status={action.status} statusTooltip={action.statusTooltip} />
|
|
36
|
+
<Button
|
|
37
|
+
size="small"
|
|
38
|
+
onClick={handleClick}
|
|
39
|
+
disabled={action.disabled}
|
|
40
|
+
appearance={buttonAppearance}
|
|
41
|
+
>
|
|
42
|
+
{action.name}
|
|
43
|
+
</Button>
|
|
44
|
+
{action.disabled && (
|
|
45
|
+
<DisabledActionHelper>{action.disableTooltipContent?.()}</DisabledActionHelper>
|
|
46
|
+
)}
|
|
47
|
+
</Flex>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const ActionChip: FC<{ status?: ActionStatus; statusTooltip?: string }> = ({
|
|
52
|
+
status,
|
|
53
|
+
statusTooltip,
|
|
54
|
+
}) => {
|
|
55
|
+
if (status === undefined) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const chip = (
|
|
60
|
+
<Chip
|
|
61
|
+
label={BrandActionChipLabel[status]}
|
|
62
|
+
color={BrandActionChipColor[status]}
|
|
63
|
+
size="small"
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return statusTooltip ? (
|
|
68
|
+
<TooltipA2>
|
|
69
|
+
<TooltipA2.Trigger>{chip}</TooltipA2.Trigger>
|
|
70
|
+
<TooltipA2.Content>{statusTooltip}</TooltipA2.Content>
|
|
71
|
+
</TooltipA2>
|
|
72
|
+
) : (
|
|
73
|
+
chip
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const DisabledActionHelper: FC<PropsWithChildren> = ({ children }) => (
|
|
78
|
+
<TooltipA2>
|
|
79
|
+
<TooltipA2.Trigger>
|
|
80
|
+
<Button icon={HelpIcon} size="small" />
|
|
81
|
+
</TooltipA2.Trigger>
|
|
82
|
+
<TooltipA2.Content>{children}</TooltipA2.Content>
|
|
83
|
+
</TooltipA2>
|
|
84
|
+
);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
|
|
4
|
+
import { Grid } from '@servicetitan/anvil2';
|
|
5
|
+
|
|
6
|
+
import { BrandCard } from '../brand-card/brand-card';
|
|
7
|
+
import { BrandCardProps } from '../../../utils/interfaces';
|
|
8
|
+
|
|
9
|
+
import * as Styles from '../styles.module.less';
|
|
10
|
+
|
|
11
|
+
export const CardsGrid: FC<{
|
|
12
|
+
brands: BrandCardProps[];
|
|
13
|
+
qaIdentifier: string;
|
|
14
|
+
}> = ({ brands, qaIdentifier }) => (
|
|
15
|
+
<Grid className={classNames(Styles.grid, `qa-${qaIdentifier}-cards-grid`)} gap="6">
|
|
16
|
+
{brands.map(brand => (
|
|
17
|
+
<BrandCard key={brand.id} {...brand} />
|
|
18
|
+
))}
|
|
19
|
+
</Grid>
|
|
20
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './cards-grid/cards-grid';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
@import (reference) '@servicetitan/tokens/dist/tokens.less';
|
|
2
|
+
|
|
3
|
+
@action-menu-item-padding: 12px;
|
|
4
|
+
@card-min-width: 330px;
|
|
5
|
+
@card-max-width: 700px;
|
|
6
|
+
@max-number-of-cards: 3;
|
|
7
|
+
|
|
8
|
+
.delete-tooltip {
|
|
9
|
+
min-width: 327px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.grid {
|
|
13
|
+
grid-template-columns: repeat(auto-fill, minmax(@card-min-width, 1fr));
|
|
14
|
+
max-width: calc(@card-max-width * @max-number-of-cards + @spacing-3 * 2);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@media (min-width: 1120px) {
|
|
18
|
+
.grid {
|
|
19
|
+
grid-template-columns: repeat(@max-number-of-cards - 1, 1fr);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@media (min-width: 1620px) {
|
|
24
|
+
.grid {
|
|
25
|
+
grid-template-columns: repeat(@max-number-of-cards, 1fr);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.brand-card {
|
|
30
|
+
height: 200px;
|
|
31
|
+
min-width: @card-min-width;
|
|
32
|
+
max-width: @card-max-width;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.card-content {
|
|
36
|
+
width: 100%;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.card-left-section-texts {
|
|
40
|
+
min-width: 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.brand-img {
|
|
44
|
+
width: @spacing-6;
|
|
45
|
+
height: @spacing-6;
|
|
46
|
+
min-width: @spacing-6;
|
|
47
|
+
min-height: @spacing-6;
|
|
48
|
+
border-radius: @border-radius-circular;
|
|
49
|
+
border: solid 1px @color-neutral-60;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.brand-img-placeholder {
|
|
53
|
+
.brand-img();
|
|
54
|
+
background-color: @color-neutral-60;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.card-with-error {
|
|
58
|
+
border-color: @color-red-500;
|
|
59
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const __esModule: true;
|
|
2
|
+
export const brandCard: string;
|
|
3
|
+
export const brandImg: string;
|
|
4
|
+
export const brandImgPlaceholder: string;
|
|
5
|
+
export const cardContent: string;
|
|
6
|
+
export const cardLeftSectionTexts: string;
|
|
7
|
+
export const cardWithError: string;
|
|
8
|
+
export const deleteTooltip: string;
|
|
9
|
+
export const grid: string;
|
|
10
|
+
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { JSX } from 'react';
|
|
2
|
+
import { ActionStatus } from '../enums/brands';
|
|
3
|
+
|
|
4
|
+
export interface BrandAction {
|
|
5
|
+
name: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
disableTooltipContent?: () => JSX.Element | string;
|
|
8
|
+
href?: string;
|
|
9
|
+
status?: ActionStatus;
|
|
10
|
+
statusTooltip?: string;
|
|
11
|
+
action?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface BrandCardProps {
|
|
15
|
+
id: string;
|
|
16
|
+
hasError?: boolean;
|
|
17
|
+
actions: BrandAction | BrandAction[];
|
|
18
|
+
logo?: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
isDefault: boolean;
|
|
21
|
+
email?: string;
|
|
22
|
+
isShared?: boolean;
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ActionStatus } from '../enums/brands';
|
|
2
|
+
import { tokens } from '@servicetitan/tokens/core';
|
|
3
|
+
|
|
4
|
+
export const BrandActionChipLabel: Record<ActionStatus, string> = {
|
|
5
|
+
[ActionStatus.Pending]: 'Pending',
|
|
6
|
+
[ActionStatus.Error]: 'Error',
|
|
7
|
+
[ActionStatus.Approved]: 'Complete',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const BrandActionChipColor: Record<ActionStatus, string> = {
|
|
11
|
+
[ActionStatus.Pending]: tokens.colorOrange200,
|
|
12
|
+
[ActionStatus.Error]: tokens.colorRed500,
|
|
13
|
+
[ActionStatus.Approved]: tokens.colorGreen200,
|
|
14
|
+
};
|