Skip to content

Commit 0587380

Browse files
Add Usage per connection block (#7102)
* Add Usage per connection block * Update Connection cell in Usage per connection table * Update Usage cell in Usage per connection table * Get list of Source and Destinations Definitions * Fix division by 0 Co-authored-by: Julia Astapenko <[email protected]>
1 parent ee559a4 commit 0587380

File tree

11 files changed

+385
-35
lines changed

11 files changed

+385
-35
lines changed

airbyte-webapp/src/components/ContentCard/ContentCard.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,22 @@ type IProps = {
88
className?: string;
99
onClick?: () => void;
1010
full?: boolean;
11-
$light?: boolean;
11+
light?: boolean;
1212
};
1313

14-
const Title = styled(H5)<{ $light?: boolean }>`
15-
padding: ${({ $light }) => ($light ? "19px 20px 20px" : "25px 25px 22px")};
14+
const Title = styled(H5)<{ light?: boolean }>`
15+
padding: ${({ light }) => (light ? "19px 20px 20px" : "25px 25px 22px")};
1616
color: ${({ theme }) => theme.darkPrimaryColor};
17-
box-shadow: ${({ $light, theme }) =>
18-
$light ? "none" : `0 1px 2px ${theme.shadowColor}`};
17+
box-shadow: ${({ light, theme }) =>
18+
light ? "none" : `0 1px 2px ${theme.shadowColor}`};
1919
font-weight: 600;
2020
letter-spacing: 0.008em;
2121
border-radius: 10px 10px 0 0;
2222
`;
2323

2424
const ContentCard: React.FC<IProps> = (props) => (
2525
<Card className={props.className} onClick={props.onClick} full={props.full}>
26-
{props.title ? <Title $light={props.$light}>{props.title}</Title> : null}
26+
{props.title ? <Title light={props.light}>{props.title}</Title> : null}
2727
{props.children}
2828
</Card>
2929
);

airbyte-webapp/src/components/Table/Table.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,17 @@ type IThProps = {
2525
highlighted?: boolean;
2626
collapse?: boolean;
2727
customWidth?: number;
28+
light?: boolean;
2829
} & React.ThHTMLAttributes<HTMLTableHeaderCellElement>;
2930

30-
const TableView = styled(Card).attrs({ as: "table" })`
31+
const TableView = styled(Card).attrs({ as: "table" })<{ light?: boolean }>`
3132
border-spacing: 0;
3233
width: 100%;
3334
max-width: 100%;
3435
border-radius: 10px;
36+
box-shadow: ${({ light, theme }) =>
37+
light ? "none" : `0 2px 4px ${theme.cardShadowColor}`};
38+
};
3539
`;
3640

3741
const Tr = styled.tr<{
@@ -70,21 +74,22 @@ const Td = styled.td<{ collapse?: boolean; customWidth?: number }>`
7074
`;
7175

7276
const Th = styled.th<IThProps>`
73-
background: ${({ theme }) => theme.textColor};
77+
background: ${({ theme, light }) => (light ? "none" : theme.textColor)};
7478
padding: 9px 13px 10px;
7579
text-align: left;
76-
font-size: 10px;
80+
font-size: ${({ light }) => (light ? 11 : 10)}px;
7781
line-height: 12px;
7882
color: ${({ theme, highlighted }) =>
7983
highlighted ? theme.whiteColor : theme.lightTextColor};
80-
border-bottom: 1px solid ${({ theme }) => theme.backgroundColor};
84+
border-bottom: ${({ theme, light }) =>
85+
light ? "none" : ` 1px solid ${theme.backgroundColor}`};
8186
width: ${({ collapse, customWidth }) =>
8287
customWidth ? `${customWidth}%` : collapse ? "0.0000000001%" : "auto"};
83-
font-weight: 600;
84-
text-transform: uppercase;
88+
font-weight: ${({ light }) => (light ? 400 : 600)};
89+
text-transform: ${({ light }) => (light ? "capitalize" : "uppercase")};
8590
8691
&:first-child {
87-
padding-left: 45px;
92+
padding-left: ${({ light }) => (light ? 13 : 45)}px;
8893
border-radius: 10px 0 0;
8994
}
9095
@@ -95,6 +100,7 @@ const Th = styled.th<IThProps>`
95100
`;
96101

97102
type IProps = {
103+
light?: boolean;
98104
columns: Array<IHeaderProps | Column<Record<string, unknown>>>;
99105
erroredRows?: boolean;
100106
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -111,6 +117,7 @@ const Table: React.FC<IProps> = ({
111117
onClickRow,
112118
erroredRows,
113119
sortBy,
120+
light,
114121
}) => {
115122
const [plugins, config] = useMemo(() => {
116123
const pl = [];
@@ -138,7 +145,7 @@ const Table: React.FC<IProps> = ({
138145
);
139146

140147
return (
141-
<TableView {...getTableProps()}>
148+
<TableView {...getTableProps()} light={light}>
142149
<thead>
143150
{headerGroups.map((headerGroup, key) => (
144151
<tr
@@ -152,6 +159,7 @@ const Table: React.FC<IProps> = ({
152159
collapse={column.collapse}
153160
customWidth={column.customWidth}
154161
key={`table-column-${key}-${columnKey}`}
162+
light={light}
155163
>
156164
{column.render("Header")}
157165
</Th>

airbyte-webapp/src/hooks/services/useDestinationDefinition.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,10 @@ const useDestinationDefinitionList = (): {
1414
});
1515
};
1616

17-
export { useDestinationDefinitionList };
17+
const useDestinationDefinition = (id: string): DestinationDefinition => {
18+
return useResource(DestinationDefinitionResource.detailShape(), {
19+
destinationDefinitionId: id,
20+
});
21+
};
22+
23+
export { useDestinationDefinitionList, useDestinationDefinition };

airbyte-webapp/src/hooks/services/useSourceDefinition.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,10 @@ const useSourceDefinitionList = (): {
1414
});
1515
};
1616

17-
export { useSourceDefinitionList };
17+
const useSourceDefinition = (id: string): SourceDefinition => {
18+
return useResource(SourceDefinitionResource.detailShape(), {
19+
sourceDefinitionId: id,
20+
});
21+
};
22+
23+
export { useSourceDefinitionList, useSourceDefinition };

airbyte-webapp/src/packages/cloud/lib/domain/cloudWorkspaces/types.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@ export interface CloudWorkspace {
55
remainingCredits: number;
66
}
77

8+
export interface CreditConsumptionByConnector {
9+
connectionId: string;
10+
creditsConsumed: number;
11+
destinationConnectionName: string;
12+
destinationDefinitionId: string;
13+
destinationDefinitionName: string;
14+
destinationId: string;
15+
sourceConnectionName: string;
16+
sourceDefinitionId: string;
17+
sourceDefinitionName: string;
18+
sourceId: string;
19+
}
20+
821
export interface CloudWorkspaceUsage {
922
workspaceId: string;
10-
creditConsumptionByConnector: {
11-
connectionId: string;
12-
creditsConsumed: number;
13-
}[];
23+
creditConsumptionByConnector: CreditConsumptionByConnector[];
1424
creditConsumptionByDay: {
1525
date: [number, number, number];
1626
creditsConsumed: number;

airbyte-webapp/src/packages/cloud/locales/en.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@
9696
"credits.date": "Date",
9797
"credits.amount": "Credits",
9898

99+
"credits.usagePerConnection": "Usage per connection",
100+
"credits.connection": "Connection",
101+
"credits.usage": "Usage",
102+
"credits.noData": "Data is empty",
103+
99104
"firebase.auth.success": "A new confirmation email has been sent to you",
100105
"firebase.auth.error.invalidPassword": "Incorrect password",
101106
"firebase.auth.error.networkRequestFailed": "There appears to be a network issue. Please try again later.",
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React from "react";
2+
import styled from "styled-components";
3+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4+
import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
5+
6+
import ImageBlock from "components/ImageBlock";
7+
8+
type ConnectionCellProps = {
9+
sourceDefinitionName: string;
10+
destinationDefinitionName: string;
11+
sourceIcon?: string;
12+
destinationIcon?: string;
13+
};
14+
15+
const Icon = styled(ImageBlock)`
16+
margin-right: 12px;
17+
display: inline-block;
18+
vertical-align: middle;
19+
height: 20px;
20+
width: 20px;
21+
`;
22+
23+
const Connector = styled.div`
24+
font-weight: 500;
25+
font-size: 14px;
26+
line-height: 25px;
27+
`;
28+
29+
const Arrow = styled(FontAwesomeIcon)`
30+
margin: 0 17px 0 9px;
31+
font-size: 9px;
32+
`;
33+
34+
const ConnectionCell: React.FC<ConnectionCellProps> = ({
35+
sourceDefinitionName,
36+
destinationDefinitionName,
37+
sourceIcon,
38+
destinationIcon,
39+
}) => {
40+
return (
41+
<>
42+
<Connector>
43+
<Icon small img={sourceIcon} />
44+
{sourceDefinitionName}
45+
</Connector>
46+
<Connector>
47+
<Arrow icon={faArrowRight} />
48+
<Icon small img={destinationIcon} />
49+
{destinationDefinitionName}
50+
</Connector>
51+
</>
52+
);
53+
};
54+
55+
export default ConnectionCell;

airbyte-webapp/src/packages/cloud/views/credits/CreditsPage/components/CreditsUsagePage.tsx

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import styled from "styled-components";
44

55
import ContentCard from "components/ContentCard";
66
import BarChart from "components/BarChart";
7+
import UsagePerConnectionTable from "./UsagePerConnectionTable";
78
import { useCurrentWorkspace } from "hooks/services/useWorkspace";
89
import { useGetUsage } from "packages/cloud/services/workspaces/WorkspacesService";
910

@@ -13,6 +14,19 @@ export const ChartWrapper = styled.div`
1314
padding: 0 50px 24px 0;
1415
`;
1516

17+
const CardBlock = styled(ContentCard)`
18+
margin: 10px 0 20px;
19+
`;
20+
21+
const Empty = styled.div`
22+
height: 100%;
23+
display: flex;
24+
justify-content: center;
25+
align-items: center;
26+
font-weight: 600;
27+
padding-bottom: 20px;
28+
`;
29+
1630
const LegendLabels = ["value"];
1731

1832
const CreditsUsagePage: React.FC = () => {
@@ -37,20 +51,43 @@ const CreditsUsagePage: React.FC = () => {
3751
);
3852

3953
return (
40-
<ContentCard title={<FormattedMessage id="credits.totalUsage" />} $light>
41-
<ChartWrapper>
42-
<BarChart
43-
data={chartData}
44-
legendLabels={LegendLabels}
45-
xLabel={formatMessage({
46-
id: "credits.date",
47-
})}
48-
yLabel={formatMessage({
49-
id: "credits.amount",
50-
})}
51-
/>
52-
</ChartWrapper>
53-
</ContentCard>
54+
<>
55+
<ContentCard title={<FormattedMessage id="credits.totalUsage" />} light>
56+
<ChartWrapper>
57+
{data && data.creditConsumptionByDay.length ? (
58+
<BarChart
59+
data={chartData}
60+
legendLabels={LegendLabels}
61+
xLabel={formatMessage({
62+
id: "credits.date",
63+
})}
64+
yLabel={formatMessage({
65+
id: "credits.amount",
66+
})}
67+
/>
68+
) : (
69+
<Empty>
70+
<FormattedMessage id="credits.noData" />
71+
</Empty>
72+
)}
73+
</ChartWrapper>
74+
</ContentCard>
75+
76+
<CardBlock
77+
title={<FormattedMessage id="credits.usagePerConnection" />}
78+
light
79+
>
80+
{data && data.creditConsumptionByConnector.length ? (
81+
<UsagePerConnectionTable
82+
creditConsumption={data.creditConsumptionByConnector}
83+
/>
84+
) : (
85+
<Empty>
86+
<FormattedMessage id="credits.noData" />
87+
</Empty>
88+
)}
89+
</CardBlock>
90+
</>
5491
);
5592
};
5693

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from "react";
2+
import styled from "styled-components";
3+
4+
type ConnectionCellProps = {
5+
value: number;
6+
percent: number;
7+
};
8+
9+
const Content = styled.div`
10+
display: flex;
11+
flex-direction: row;
12+
align-items: center;
13+
`;
14+
15+
const Value = styled.div`
16+
padding-right: 10px;
17+
font-weight: 500;
18+
font-size: 14px;
19+
line-height: 17px;
20+
flex: 1 0 0;
21+
min-width: 50px;
22+
`;
23+
24+
const Bar = styled.div`
25+
height: 10px;
26+
width: 100%;
27+
background: ${({ theme }) => theme.greyColor20};
28+
flex: 10 0 0;
29+
overflow: hidden;
30+
border-radius: 0 4px 4px 0;
31+
`;
32+
33+
const Full = styled.div<{ percent: number }>`
34+
height: 100%;
35+
width: ${({ percent }) => percent}%;
36+
background: ${({ theme }) => theme.lightTextColor};
37+
opacity: 0.5;
38+
`;
39+
40+
const UsageCell: React.FC<ConnectionCellProps> = ({ value, percent }) => {
41+
return (
42+
<Content>
43+
<Value>{value}</Value>
44+
<Bar>
45+
<Full percent={percent} />
46+
</Bar>
47+
</Content>
48+
);
49+
};
50+
51+
export default UsageCell;

0 commit comments

Comments
 (0)