@stackone/hub 0.1.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/.github/workflows/node-ci.yml +20 -0
- package/.github/workflows/release-please.yml +37 -0
- package/.github/workflows/semantic-pull-request.yml +31 -0
- package/.nvmrc +1 -0
- package/.release-please-manifest.json +1 -0
- package/.yalc/@stackone/malachite/README.md +1 -0
- package/.yalc/@stackone/malachite/package.json +37 -0
- package/.yalc/@stackone/malachite/yalc.sig +1 -0
- package/CHANGELOG.md +30 -0
- package/README.md +225 -0
- package/biome.json +77 -0
- package/dev/index.html +11 -0
- package/dev/main.css +80 -0
- package/dev/main.tsx +96 -0
- package/dev/vite-env.d.ts +15 -0
- package/index.html +14 -0
- package/package.json +44 -0
- package/release-please-config.json +5 -0
- package/rollup.config.mjs +72 -0
- package/src/StackOneHub.tsx +99 -0
- package/src/WebComponentWrapper.tsx +14 -0
- package/src/index.ts +1 -0
- package/src/modules/csv-importer.tsx/CsvImporter.tsx +35 -0
- package/src/modules/integration-picker/IntegrationPicker.tsx +89 -0
- package/src/modules/integration-picker/components/IntegrationFields.tsx +115 -0
- package/src/modules/integration-picker/components/IntegrationList.tsx +71 -0
- package/src/modules/integration-picker/components/IntegrationPickerContent.tsx +107 -0
- package/src/modules/integration-picker/components/cardFooter.tsx +88 -0
- package/src/modules/integration-picker/components/cardTitle.tsx +51 -0
- package/src/modules/integration-picker/components/views/ErrorView.tsx +9 -0
- package/src/modules/integration-picker/components/views/IntegrationFormView.tsx +22 -0
- package/src/modules/integration-picker/components/views/IntegrationListView.tsx +19 -0
- package/src/modules/integration-picker/components/views/LoadingView.tsx +11 -0
- package/src/modules/integration-picker/components/views/SuccessView.tsx +10 -0
- package/src/modules/integration-picker/components/views/index.ts +5 -0
- package/src/modules/integration-picker/hooks/useIntegrationPicker.ts +221 -0
- package/src/modules/integration-picker/queries.ts +78 -0
- package/src/modules/integration-picker/types.ts +60 -0
- package/src/shared/categories.ts +55 -0
- package/src/shared/components/error.tsx +33 -0
- package/src/shared/components/errorBoundary.tsx +31 -0
- package/src/shared/components/loading.tsx +30 -0
- package/src/shared/components/success.tsx +33 -0
- package/src/shared/httpClient.ts +79 -0
- package/src/types/types.ts +1 -0
- package/tsconfig.json +19 -0
- package/vite.config.ts +11 -0
- package/yalc.lock +9 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
class ErrorBoundary extends React.Component<
|
|
4
|
+
{ fallback: React.ReactNode; children: React.ReactNode },
|
|
5
|
+
{ hasError: boolean }
|
|
6
|
+
> {
|
|
7
|
+
constructor(props: { fallback: React.ReactNode; children: React.ReactNode }) {
|
|
8
|
+
super(props);
|
|
9
|
+
this.state = { hasError: false };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static getDerivedStateFromError(_error: Error) {
|
|
13
|
+
// Update state so the next render will show the fallback UI.
|
|
14
|
+
return { hasError: true };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
componentDidCatch(_error: Error, _info: React.ErrorInfo) {
|
|
18
|
+
// TODO: Log error to Sentry
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
render() {
|
|
22
|
+
if (this.state.hasError) {
|
|
23
|
+
// You can render any custom fallback UI
|
|
24
|
+
return this.props.fallback;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return this.props.children;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default ErrorBoundary;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Flex,
|
|
3
|
+
FlexAlign,
|
|
4
|
+
FlexDirection,
|
|
5
|
+
FlexGapSize,
|
|
6
|
+
FlexJustify,
|
|
7
|
+
Spinner,
|
|
8
|
+
Typography,
|
|
9
|
+
} from '@stackone/malachite';
|
|
10
|
+
|
|
11
|
+
export const Loading: React.FC<{
|
|
12
|
+
title: string;
|
|
13
|
+
description: string;
|
|
14
|
+
}> = ({ title, description }) => {
|
|
15
|
+
return (
|
|
16
|
+
<Flex
|
|
17
|
+
justify={FlexJustify.Center}
|
|
18
|
+
align={FlexAlign.Center}
|
|
19
|
+
direction={FlexDirection.Vertical}
|
|
20
|
+
gapSize={FlexGapSize.Small}
|
|
21
|
+
fullHeight
|
|
22
|
+
>
|
|
23
|
+
<Spinner size="xxxsmall" />
|
|
24
|
+
<Typography.Text fontWeight="bold" size="large">
|
|
25
|
+
{title}
|
|
26
|
+
</Typography.Text>
|
|
27
|
+
<Typography.SecondaryText>{description}</Typography.SecondaryText>
|
|
28
|
+
</Flex>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CustomIcons,
|
|
3
|
+
Flex,
|
|
4
|
+
FlexAlign,
|
|
5
|
+
FlexDirection,
|
|
6
|
+
FlexGapSize,
|
|
7
|
+
FlexJustify,
|
|
8
|
+
Typography,
|
|
9
|
+
} from '@stackone/malachite';
|
|
10
|
+
|
|
11
|
+
interface SuccessProps {
|
|
12
|
+
integrationName: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Success: React.FC<SuccessProps> = ({ integrationName }) => (
|
|
16
|
+
<Flex
|
|
17
|
+
justify={FlexJustify.Center}
|
|
18
|
+
align={FlexAlign.Center}
|
|
19
|
+
direction={FlexDirection.Vertical}
|
|
20
|
+
gapSize={FlexGapSize.Small}
|
|
21
|
+
fullHeight
|
|
22
|
+
>
|
|
23
|
+
<CustomIcons.CheckCircleFilledIcon />
|
|
24
|
+
<Typography.Text fontWeight="bold" size="large">
|
|
25
|
+
Connection Successful
|
|
26
|
+
</Typography.Text>
|
|
27
|
+
<Typography.SecondaryText>
|
|
28
|
+
Account successfully connected to {integrationName}
|
|
29
|
+
</Typography.SecondaryText>
|
|
30
|
+
</Flex>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export default Success;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export interface ErrorDetails {
|
|
2
|
+
message: string;
|
|
3
|
+
statusCode: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export const isErrorsDetails = (error: unknown): error is ErrorDetails => {
|
|
7
|
+
return (error as ErrorDetails).statusCode !== undefined;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export interface RequestParams {
|
|
11
|
+
url: string;
|
|
12
|
+
body?: Record<string, unknown>;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
requestFn?: typeof fetch;
|
|
15
|
+
logger?: Console;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const isTokenExpired = (response: Response): boolean => {
|
|
19
|
+
if (response?.status === 401 || response?.status === 403) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export async function request<T>({
|
|
26
|
+
url,
|
|
27
|
+
body,
|
|
28
|
+
headers,
|
|
29
|
+
method,
|
|
30
|
+
requestFn = fetch,
|
|
31
|
+
logger = console,
|
|
32
|
+
}: RequestParams & { method: 'PATCH' | 'POST' | 'DELETE' | 'GET' }): Promise<T | null> {
|
|
33
|
+
try {
|
|
34
|
+
const response = await requestFn(url, {
|
|
35
|
+
method,
|
|
36
|
+
headers,
|
|
37
|
+
body: JSON.stringify(body),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (isTokenExpired(response)) {
|
|
41
|
+
logger.warn('Token expired');
|
|
42
|
+
return null;
|
|
43
|
+
} else {
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
const errorResponse = (await response.json()) as ErrorDetails;
|
|
46
|
+
if (errorResponse && isErrorsDetails(errorResponse)) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
JSON.stringify({
|
|
49
|
+
status: errorResponse.statusCode,
|
|
50
|
+
message: errorResponse.message,
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (await response.json()) as T;
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.error(`Error making request to ${url}`, error);
|
|
60
|
+
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function postRequest<T>(params: RequestParams): Promise<T | null> {
|
|
66
|
+
return request<T>({ ...params, method: 'POST' });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function patchRequest<T>(params: RequestParams): Promise<T | null> {
|
|
70
|
+
return request<T>({ ...params, method: 'PATCH' });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function deleteRequest<T>(params: RequestParams): Promise<T | null> {
|
|
74
|
+
return request<T>({ ...params, method: 'DELETE' });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function getRequest<T>(params: RequestParams): Promise<T | null> {
|
|
78
|
+
return request<T>({ ...params, method: 'GET' });
|
|
79
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type HubModes = 'integration-picker' | 'csv-importer';
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES6",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"jsx": "react-jsx",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"moduleResolution": "Node",
|
|
10
|
+
"declaration": false,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"sourceMap": true,
|
|
13
|
+
"jsxImportSource": "react",
|
|
14
|
+
"forceConsistentCasingInFileNames": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src"],
|
|
17
|
+
"exclude": ["**/*.d.ts"],
|
|
18
|
+
"files": ["src/index.ts"]
|
|
19
|
+
}
|
package/vite.config.ts
ADDED