Skip to content

Commit 7472d8d

Browse files
authored
Merge pull request #95 from AlphadayHQ/dev
Update main
2 parents 230e416 + 23ea0ff commit 7472d8d

File tree

10 files changed

+583
-28
lines changed

10 files changed

+583
-28
lines changed

.github/workflows/basic-checks.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Basic Checks Dev
2+
3+
on:
4+
pull_request:
5+
types: ready_for_review
6+
branches: [dev]
7+
env:
8+
ENVIRONMENT: test
9+
10+
VITE_ZAPPER_BASE_URL: https://api.zapper.fi/v1/
11+
VITE_ZAPPER_BASE_URL_V2: https://api.zapper.fi/v2/
12+
VITE_APP_ZAPPER_API_KEY: ${{ secrets.STAGING__FRONTEND__API_KEY_ZAPPER }}
13+
14+
jobs:
15+
basic-checks:
16+
if: ${{ !(contains(github.event.pull_request.title, '(skip-ci)')) }}
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v2
20+
with:
21+
ref: ${{ github.event.pull_request.head.sha }}
22+
- uses: actions/setup-node@v3
23+
with:
24+
node-version: "18.15.0"
25+
cache: "yarn"
26+
cache-dependency-path: |
27+
yarn.lock
28+
- name: "Log current branch info"
29+
run: |
30+
echo "On branch $( git rev-parse --abbrev-ref HEAD )"
31+
echo "On commit $( git rev-parse --short HEAD )"
32+
- name: "Check node env"
33+
run: |
34+
echo "node version $( node --version )"
35+
echo "yarn version $( yarn --version )"
36+
- run: yarn
37+
- run: yarn typecheck
38+
- run: yarn lint

.github/workflows/deploy-dev.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ jobs:
7676
VITE_FIRE_STORAGE_BUCKET: ${{ secrets.DEV__REACT_APP_FIRE_STORAGE_BUCKET }}
7777
VITE_FIRE_MESSAGE_SENDER_ID: ${{ secrets.DEV__REACT_APP_FIRE_MESSAGE_SENDER_ID }}
7878
VITE_FIRE_APP_ID: ${{ secrets.DEV__REACT_APP_FIRE_APP_ID }}
79+
VITE_SENTRY_DSN: ${{ secrets.PROD__VITE_SENTRY_DSN }}
7980

8081
NODE_OPTIONS: "--max_old_space_size=4096"
8182

packages/frontend/src/api/services/views/viewsEndpoints.ts

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import queryString from "query-string";
2+
import {
3+
validateCustomData,
4+
validateCustomMeta,
5+
} from "src/api/utils/customDataUtils";
26
import { Logger } from "src/api/utils/logging";
37
import { TEMPLATES_DICT } from "src/types";
48
import CONFIG from "../../../config/config";
@@ -34,26 +38,53 @@ import {
3438
TSaveViewMetaRequest,
3539
TViewForWalletRequest,
3640
TViewForWalletResponse,
41+
TRemoteRawUserView,
42+
TViewByIdRawResponse,
43+
TViewByHashOrSlugRawResponse,
44+
TViewForWalletRawResponse,
3745
} from "./types";
3846

3947
const { VIEWS } = CONFIG.API.DEFAULT.ROUTES;
4048

41-
const viewCheck = (view: TRemoteUserView): TRemoteUserView => {
49+
const viewCheck = (
50+
view: Readonly<TRemoteRawUserView>
51+
): Readonly<TRemoteUserView> => {
4252
const viewWidgets = [...view.widgets];
4353
const filteredWidgets: TRemoteUserViewWidget[] = [];
4454
viewWidgets.forEach((viewWidget) => {
45-
if (!viewWidget.widget.template) {
46-
Logger.warn(
47-
"viewsEndpoints::viewCheck: widget with no template found:",
48-
viewWidget.widget.slug
49-
);
50-
} else if (!(viewWidget.widget.template.slug in TEMPLATES_DICT)) {
55+
const widgetSlug = viewWidget.widget.template.slug;
56+
if (!(widgetSlug in TEMPLATES_DICT)) {
5157
Logger.warn(
5258
"viewsEndpoints::viewCheck: unknown widget template found:",
5359
viewWidget.widget.template.slug
5460
);
5561
} else {
56-
filteredWidgets.push(viewWidget);
62+
const dataValidationResult = validateCustomData(
63+
viewWidget.widget.custom_data
64+
);
65+
const metaValidationResult = validateCustomMeta(
66+
viewWidget.widget.custom_meta
67+
);
68+
if (
69+
dataValidationResult.errorCode ||
70+
metaValidationResult.errorCode
71+
) {
72+
Logger.warn(
73+
`viewsEndpoints::viewCheck: data validation failed for widget ${widgetSlug}`,
74+
dataValidationResult.errorCode ??
75+
metaValidationResult.errorCode
76+
);
77+
}
78+
// recall: if data (meta) validation failed, data (meta) will be undefined
79+
const validWidget = {
80+
...viewWidget,
81+
widget: {
82+
...viewWidget.widget,
83+
custom_data: dataValidationResult.items,
84+
custom_meta: metaValidationResult.meta,
85+
},
86+
};
87+
filteredWidgets.push(validWidget);
5788
}
5889
});
5990
return {
@@ -98,7 +129,7 @@ const viewsApi = alphadayApi.injectEndpoints({
98129
Logger.debug("getViewById: querying", path);
99130
return path;
100131
},
101-
transformResponse: (r: TViewByIdResponse): TViewByIdResponse =>
132+
transformResponse: (r: TViewByIdRawResponse): TViewByIdResponse =>
102133
viewCheck(r),
103134
}),
104135
getViewByHash: builder.query<
@@ -112,7 +143,7 @@ const viewsApi = alphadayApi.injectEndpoints({
112143
return path;
113144
},
114145
transformResponse: (
115-
r: TViewByHashOrSlugResponse
146+
r: TViewByHashOrSlugRawResponse
116147
): TViewByHashOrSlugResponse => viewCheck(r),
117148
providesTags: ["CurrentView"],
118149
}),
@@ -126,8 +157,8 @@ const viewsApi = alphadayApi.injectEndpoints({
126157
return path;
127158
},
128159
transformResponse: (
129-
r: TViewByHashOrSlugResponse
130-
): TViewByHashOrSlugResponse => viewCheck(r),
160+
r: TViewForWalletRawResponse
161+
): TViewForWalletResponse => viewCheck(r),
131162
providesTags: ["CurrentView"],
132163
}),
133164
getViewCategories: builder.query<

packages/frontend/src/api/utils/customDataUtils.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import {
66
TRemoteCustomData,
77
TRemoteCustomDatum,
88
TRemoteCustomMeta,
9-
TCustomMetaCard,
109
TCustomMetaChart,
10+
TCustomMetaCard,
1111
} from "src/api/services";
1212
import { TCustomItem, TCustomSeries } from "src/api/types";
1313
import { getErrorMessage } from "src/api/utils/errorHandling";
@@ -147,7 +147,11 @@ export const validateCustomMeta: (
147147
errorCode: EWidgetValidationErrorCodes.MissingField,
148148
};
149149
}
150-
if (meta.layout_type !== "table" && meta.layout_type !== "chart") {
150+
if (
151+
meta.layout_type !== "table" &&
152+
meta.layout_type !== "chart" &&
153+
meta.layout_type !== "card"
154+
) {
151155
return {
152156
meta: undefined,
153157
errorCode: EWidgetValidationErrorCodes.InvalidDataFieldType,
@@ -310,26 +314,27 @@ export const formatCustomDataField: (
310314
}
311315
};
312316

313-
type TJustification = "flex-start" | "flex-end" | "center";
317+
// these correspond to tailwind jargon
318+
type TJustification = "justify-start" | "justify-end" | "justify-center";
314319
export const getColumnJustification: (
315320
f: TRemoteFormat,
316321
j?: "left" | "center" | "right" | undefined
317322
) => TJustification | undefined = (format, justify) => {
318323
if (justify) {
319324
switch (justify) {
320325
case "left":
321-
return "flex-start";
326+
return "justify-start";
322327
case "center":
323-
return "center";
328+
return "justify-center";
324329
case "right":
325-
return "flex-end";
330+
return "justify-end";
326331
default:
327332
}
328333
}
329334
if (format && ["number", "decimal", "currency"].indexOf(format) !== -1) {
330-
return "flex-end";
335+
return "justify-end";
331336
}
332-
return "flex-start";
337+
return "justify-start";
333338
};
334339

335340
export const customDataAsSeries: (items: TRemoteCustomData) => TCustomSeries = (
@@ -448,7 +453,7 @@ export const getYSeries: (
448453
*/
449454
export const customDataAsCardData: (
450455
customData: TRemoteCustomData,
451-
meta: TCustomMetaCard | undefined,
456+
customMeta: TCustomMetaCard | undefined,
452457
widgetName: string
453458
) => { title?: string; value?: string | React.ReactNode } | undefined = (
454459
customData,

packages/frontend/src/api/utils/errorHandling.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import logoDay from "@alphaday/ui-kit/src/assets/svg/logo-white.svg";
22
import { SerializedError } from "@reduxjs/toolkit";
33
import { FetchBaseQueryError } from "@reduxjs/toolkit/query/react";
44
import wideAlphaImage from "src/assets/placeholders/wide-alpha.png";
5-
5+
import CONFIG from "src/config";
66
import globalMessages from "src/globalMessages";
7+
import { Logger } from "./logging";
78

89
/* eslint-disable no-param-reassign */
910
export const imgOnError = (e: React.SyntheticEvent<HTMLImageElement>): void => {
@@ -21,6 +22,18 @@ export const wideImgOnError = (
2122
};
2223
/* eslint-enable no-param-reassign */
2324

25+
/* eslint-disable no-param-reassign */
26+
export const handleTableImgError =
27+
(path: string | undefined) =>
28+
(e: React.SyntheticEvent<HTMLImageElement>): void => {
29+
e.currentTarget.onerror = null;
30+
e.currentTarget.src = logoDay;
31+
if (CONFIG.IS_PROD && path) {
32+
Logger.error("handleTableImgError: could not resolve image", path);
33+
}
34+
};
35+
/* eslint-enable no-param-reassign */
36+
2437
export type TErrorId = keyof Omit<
2538
(typeof globalMessages)["error"],
2639
"requestFailed" | "boardHasNoRequiredWidget"
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { FC, FormEvent, useRef, useState } from "react";
2+
import { ModuleLoader, ScrollBar } from "@alphaday/ui-kit";
3+
import { useWidgetSize } from "src/api/hooks";
4+
import {
5+
TCustomLayoutEntry,
6+
TCustomRowProps,
7+
TCustomItem,
8+
} from "src/api/types";
9+
import { shouldFetchMoreItems } from "src/api/utils/itemUtils";
10+
import CONFIG from "src/config";
11+
import { CompactTableRow, TableHeader, TableRow } from "./TableComponents";
12+
13+
const { WIDGET_HEIGHT: DEFAULT_WIDGET_HEIGHT } = CONFIG.WIDGETS.TABLE;
14+
const HEADER_HEIGHT = 31;
15+
// allow standard layout for tables of up to STD_LAYOUT_MAX_SIZE columns
16+
const STD_LAYOUT_MAX_SIZE = 4;
17+
18+
interface ICustomTableProps {
19+
items: TCustomItem[];
20+
columns: TCustomLayoutEntry[];
21+
rowProps: TCustomRowProps | undefined;
22+
widgetHeight: number;
23+
isLoadingItems: boolean;
24+
handlePaginate: (type: "next" | "previous") => void;
25+
setWidgetHeight: (size: number) => void;
26+
}
27+
28+
const CustomTableModule: FC<ICustomTableProps> = ({
29+
items,
30+
columns,
31+
rowProps,
32+
widgetHeight,
33+
isLoadingItems,
34+
handlePaginate,
35+
setWidgetHeight,
36+
}) => {
37+
const widgetSize = useWidgetSize([450]);
38+
const isCompactMode =
39+
widgetSize === "sm" || columns.length > STD_LAYOUT_MAX_SIZE;
40+
const [scrollRef, setScrollRef] = useState<HTMLElement | undefined>();
41+
const prevScrollRef = useRef<HTMLElement | undefined>();
42+
43+
if (scrollRef !== prevScrollRef.current) {
44+
if (scrollRef) {
45+
const height =
46+
Array.from(scrollRef.children).reduce(
47+
(partialSum, child) => partialSum + child.clientHeight,
48+
0
49+
) + HEADER_HEIGHT;
50+
setWidgetHeight(Math.min(height, DEFAULT_WIDGET_HEIGHT));
51+
}
52+
prevScrollRef.current = scrollRef;
53+
}
54+
55+
const handleScroll = ({ currentTarget }: FormEvent<HTMLElement>) => {
56+
if (shouldFetchMoreItems(currentTarget)) {
57+
handlePaginate("next");
58+
}
59+
};
60+
61+
const addLinkColumn = rowProps?.uri_ref !== undefined;
62+
63+
if (isLoadingItems) {
64+
return <ModuleLoader $height={`${widgetHeight}px`} />;
65+
}
66+
67+
if (items.length === 0) {
68+
return (
69+
<div className="flex flex-auto h-300 justify-center items-center">
70+
<p>No Items Found</p>
71+
</div>
72+
);
73+
}
74+
75+
return (
76+
<div className="h-25">
77+
{!isCompactMode && (
78+
<TableHeader layout={columns} addExtraColumn={addLinkColumn} />
79+
)}
80+
<ScrollBar
81+
onScroll={handleScroll}
82+
className="divide-y divide-solid divide-btnRingVariant500"
83+
containerRef={setScrollRef}
84+
style={{
85+
height: widgetHeight - HEADER_HEIGHT,
86+
}}
87+
>
88+
{items.map((item) => {
89+
return isCompactMode ? (
90+
<CompactTableRow
91+
columnsLayout={columns}
92+
rowData={item}
93+
rowProps={rowProps}
94+
key={item.id}
95+
/>
96+
) : (
97+
<TableRow
98+
columnsLayout={columns}
99+
rowData={item}
100+
rowProps={rowProps}
101+
key={item.id}
102+
/>
103+
);
104+
})}
105+
</ScrollBar>
106+
</div>
107+
);
108+
};
109+
110+
export default CustomTableModule;

0 commit comments

Comments
 (0)