@malloy-publisher/sdk 0.0.1
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/.eslintrc +36 -0
- package/.prettierrc +4 -0
- package/README.md +1 -0
- package/openapitools.json +7 -0
- package/package.json +63 -0
- package/src/assets/img/spinner.svg +43 -0
- package/src/client/.openapi-generator/FILES +9 -0
- package/src/client/.openapi-generator/VERSION +1 -0
- package/src/client/.openapi-generator-ignore +23 -0
- package/src/client/api.ts +1170 -0
- package/src/client/base.ts +86 -0
- package/src/client/common.ts +150 -0
- package/src/client/configuration.ts +110 -0
- package/src/client/git_push.sh +57 -0
- package/src/client/index.ts +18 -0
- package/src/components/DOMElement/DOMElement.tsx +19 -0
- package/src/components/DOMElement/index.ts +1 -0
- package/src/components/Model/Model.tsx +110 -0
- package/src/components/Model/ModelCell.tsx +175 -0
- package/src/components/Model/index.ts +1 -0
- package/src/components/Notebook/Notebook.tsx +82 -0
- package/src/components/Notebook/NotebookCell.tsx +194 -0
- package/src/components/Notebook/index.ts +1 -0
- package/src/components/Package/Config.tsx +99 -0
- package/src/components/Package/Databases.tsx +75 -0
- package/src/components/Package/FileTreeView.tsx +224 -0
- package/src/components/Package/Models.tsx +77 -0
- package/src/components/Package/Package.tsx +78 -0
- package/src/components/Package/index.ts +1 -0
- package/src/components/Project/About.tsx +56 -0
- package/src/components/Project/Packages.tsx +105 -0
- package/src/components/Project/Project.tsx +26 -0
- package/src/components/Project/index.ts +1 -0
- package/src/components/QueryResult/QueryResult.tsx +84 -0
- package/src/components/QueryResult/index.ts +1 -0
- package/src/components/RenderedResult/RenderedResult.tsx +38 -0
- package/src/components/highlighter.ts +649 -0
- package/src/components/index.ts +5 -0
- package/src/components/styles.ts +21 -0
- package/src/index.ts +1 -0
- package/src/react-app-env.d.ts +73 -0
- package/tsconfig.json +34 -0
- package/vite.config.ts +31 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
TreeItem2Content,
|
|
4
|
+
TreeItem2Label,
|
|
5
|
+
TreeItem2Root,
|
|
6
|
+
TreeItem2IconContainer,
|
|
7
|
+
} from "@mui/x-tree-view/TreeItem2";
|
|
8
|
+
import { RichTreeView } from "@mui/x-tree-view/RichTreeView";
|
|
9
|
+
import { TreeItem2Provider } from "@mui/x-tree-view/TreeItem2Provider";
|
|
10
|
+
import { TreeViewBaseItem } from "@mui/x-tree-view/models";
|
|
11
|
+
import {
|
|
12
|
+
unstable_useTreeItem2 as useTreeItem2,
|
|
13
|
+
UseTreeItem2Parameters,
|
|
14
|
+
} from "@mui/x-tree-view/useTreeItem2";
|
|
15
|
+
import { TreeItem2Icon } from "@mui/x-tree-view/TreeItem2Icon";
|
|
16
|
+
import FolderIcon from "@mui/icons-material/FolderOutlined";
|
|
17
|
+
import ArticleIcon from "@mui/icons-material/ArticleOutlined";
|
|
18
|
+
import ErrorIcon from "@mui/icons-material/ErrorOutlined";
|
|
19
|
+
import { TransitionProps } from "@mui/material/transitions";
|
|
20
|
+
import { animated, useSpring } from "@react-spring/web";
|
|
21
|
+
import Collapse from "@mui/material/Collapse";
|
|
22
|
+
import DnsIcon from "@mui/icons-material/DnsOutlined";
|
|
23
|
+
import DataArrayIcon from "@mui/icons-material/DataArrayOutlined";
|
|
24
|
+
import { Database, Model } from "../../client";
|
|
25
|
+
import { Typography } from "@mui/material";
|
|
26
|
+
|
|
27
|
+
interface FiieTreeViewProps {
|
|
28
|
+
items: Model[] | Database[];
|
|
29
|
+
defaultExpandedItems: string[];
|
|
30
|
+
navigate?: (to: string) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function FileTreeView({
|
|
34
|
+
items,
|
|
35
|
+
defaultExpandedItems,
|
|
36
|
+
navigate,
|
|
37
|
+
}: FiieTreeViewProps) {
|
|
38
|
+
return (
|
|
39
|
+
<RichTreeView
|
|
40
|
+
items={getTreeView(items, navigate)}
|
|
41
|
+
defaultExpandedItems={defaultExpandedItems}
|
|
42
|
+
slots={{ item: CustomTreeItem }}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const AnimatedCollapse = animated(Collapse);
|
|
48
|
+
|
|
49
|
+
function TransitionComponent(props: TransitionProps) {
|
|
50
|
+
const style = useSpring({
|
|
51
|
+
to: {
|
|
52
|
+
opacity: props.in ? 1 : 0,
|
|
53
|
+
transform: `translate3d(0,${props.in ? 0 : 20}px,0)`,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return <AnimatedCollapse style={style} {...props} />;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type FileType = "directory" | "model" | "notebook" | "database" | "unknown";
|
|
61
|
+
|
|
62
|
+
type ExtendedTreeItemProps = {
|
|
63
|
+
id: string;
|
|
64
|
+
label: string;
|
|
65
|
+
fileType: FileType;
|
|
66
|
+
selectable: boolean;
|
|
67
|
+
link: () => void | undefined;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
interface CustomLabelProps {
|
|
71
|
+
item: ExtendedTreeItemProps;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function CustomTreeItem2Label({ item, ...other }: CustomLabelProps) {
|
|
75
|
+
return (
|
|
76
|
+
<TreeItem2Label
|
|
77
|
+
{...other}
|
|
78
|
+
sx={{
|
|
79
|
+
display: "flex",
|
|
80
|
+
alignItems: "center",
|
|
81
|
+
color: "grey.600",
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
{(item.fileType === "directory" && <FolderIcon />) ||
|
|
85
|
+
(item.fileType === "notebook" && <ArticleIcon />) ||
|
|
86
|
+
(item.fileType === "model" && <DataArrayIcon />) ||
|
|
87
|
+
(item.fileType == "database" && <DnsIcon />) || <ErrorIcon />}
|
|
88
|
+
<Typography
|
|
89
|
+
variant="body2"
|
|
90
|
+
sx={{ marginLeft: "5px" }}
|
|
91
|
+
color={item.link ? "primary.main" : "grey.600"}
|
|
92
|
+
>
|
|
93
|
+
{item.label}
|
|
94
|
+
</Typography>
|
|
95
|
+
</TreeItem2Label>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface CustomTreeItemProps
|
|
100
|
+
extends Omit<UseTreeItem2Parameters, "rootRef">,
|
|
101
|
+
Omit<React.HTMLAttributes<HTMLLIElement>, "onFocus"> {}
|
|
102
|
+
|
|
103
|
+
const CustomTreeItem = React.forwardRef(function CustomTreeItem(
|
|
104
|
+
props: CustomTreeItemProps,
|
|
105
|
+
ref: React.Ref<HTMLLIElement>,
|
|
106
|
+
) {
|
|
107
|
+
const { id, itemId, label, children, disabled, ...other } = props;
|
|
108
|
+
const {
|
|
109
|
+
getRootProps,
|
|
110
|
+
getContentProps,
|
|
111
|
+
getLabelProps,
|
|
112
|
+
getIconContainerProps,
|
|
113
|
+
getGroupTransitionProps,
|
|
114
|
+
status,
|
|
115
|
+
publicAPI,
|
|
116
|
+
} = useTreeItem2({
|
|
117
|
+
id,
|
|
118
|
+
itemId,
|
|
119
|
+
children,
|
|
120
|
+
label,
|
|
121
|
+
disabled,
|
|
122
|
+
rootRef: ref,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const item = publicAPI.getItem(itemId);
|
|
126
|
+
|
|
127
|
+
// Disable select and focus. We want to either click through to a model or notebook or just exapnd
|
|
128
|
+
// collapse
|
|
129
|
+
status.selected = false;
|
|
130
|
+
status.focused = false;
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<TreeItem2Provider itemId={itemId}>
|
|
134
|
+
<TreeItem2Root {...getRootProps(other)}>
|
|
135
|
+
<TreeItem2Content
|
|
136
|
+
{...getContentProps()}
|
|
137
|
+
{...(item.link && {
|
|
138
|
+
onClick: () => item.link(),
|
|
139
|
+
})}
|
|
140
|
+
sx={{
|
|
141
|
+
// If the item is not selectable, disable pointer events.
|
|
142
|
+
pointerEvents: !item.selectable && "none",
|
|
143
|
+
}}
|
|
144
|
+
>
|
|
145
|
+
<TreeItem2IconContainer {...getIconContainerProps()}>
|
|
146
|
+
<TreeItem2Icon status={status} />
|
|
147
|
+
</TreeItem2IconContainer>
|
|
148
|
+
<CustomTreeItem2Label
|
|
149
|
+
{...getLabelProps({
|
|
150
|
+
item,
|
|
151
|
+
})}
|
|
152
|
+
/>
|
|
153
|
+
</TreeItem2Content>
|
|
154
|
+
{children && <TransitionComponent {...getGroupTransitionProps()} />}
|
|
155
|
+
</TreeItem2Root>
|
|
156
|
+
</TreeItem2Provider>
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
function getTreeView(
|
|
161
|
+
metadataEntries: Model[] | Database[],
|
|
162
|
+
navigate: (to: string) => void,
|
|
163
|
+
): TreeViewBaseItem<ExtendedTreeItemProps>[] {
|
|
164
|
+
const tree = new Map<string, unknown>();
|
|
165
|
+
metadataEntries.map((entry: Model | Database) => {
|
|
166
|
+
let node = tree;
|
|
167
|
+
const pathParts = entry.path.split("/");
|
|
168
|
+
pathParts.forEach((part, index) => {
|
|
169
|
+
if (index === pathParts.length - 1) {
|
|
170
|
+
node.set(part, entry);
|
|
171
|
+
} else if (!node.has(part)) {
|
|
172
|
+
node.set(part, new Map<string, unknown>());
|
|
173
|
+
node = node.get(part) as Map<string, unknown>;
|
|
174
|
+
} else {
|
|
175
|
+
node = node.get(part) as Map<string, unknown>;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
return getTreeViewRecursive(tree, "", navigate);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function getTreeViewRecursive(
|
|
183
|
+
node: Map<string, unknown>,
|
|
184
|
+
path: string,
|
|
185
|
+
navigate: (to: string) => void,
|
|
186
|
+
): TreeViewBaseItem<ExtendedTreeItemProps>[] {
|
|
187
|
+
const treeViewItems: TreeViewBaseItem<ExtendedTreeItemProps>[] = [];
|
|
188
|
+
node.forEach((value, key) => {
|
|
189
|
+
if ((value as Model | Database).type !== undefined) {
|
|
190
|
+
const fileType =
|
|
191
|
+
(key.endsWith(".malloy") && "model") ||
|
|
192
|
+
(key.endsWith(".malloynb") && "notebook") ||
|
|
193
|
+
(key.endsWith(".parquet") && "database") ||
|
|
194
|
+
"unknown";
|
|
195
|
+
// This is a model or database.
|
|
196
|
+
treeViewItems.push({
|
|
197
|
+
id: path + key,
|
|
198
|
+
label: key,
|
|
199
|
+
fileType: fileType,
|
|
200
|
+
link:
|
|
201
|
+
fileType === "model" || fileType === "notebook"
|
|
202
|
+
? navigate.bind(null, path + key)
|
|
203
|
+
: undefined,
|
|
204
|
+
selectable: fileType === "model" || fileType === "notebook",
|
|
205
|
+
});
|
|
206
|
+
} else {
|
|
207
|
+
// This is a directory.
|
|
208
|
+
path += `${key}/`;
|
|
209
|
+
treeViewItems.push({
|
|
210
|
+
id: path,
|
|
211
|
+
label: key,
|
|
212
|
+
fileType: "directory",
|
|
213
|
+
selectable: true,
|
|
214
|
+
link: undefined,
|
|
215
|
+
children: getTreeViewRecursive(
|
|
216
|
+
value as Map<string, unknown>,
|
|
217
|
+
path,
|
|
218
|
+
navigate,
|
|
219
|
+
),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
return treeViewItems;
|
|
224
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Box, Divider, Typography } from "@mui/material";
|
|
2
|
+
import { QueryClient, useQuery } from "@tanstack/react-query";
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
import { Configuration, ModelsApi } from "../../client";
|
|
5
|
+
import { StyledCard, StyledCardContent } from "../styles";
|
|
6
|
+
import { FileTreeView } from "./FileTreeView";
|
|
7
|
+
|
|
8
|
+
axios.defaults.baseURL = "http://localhost:4000";
|
|
9
|
+
const modelsApi = new ModelsApi(new Configuration());
|
|
10
|
+
const queryClient = new QueryClient();
|
|
11
|
+
|
|
12
|
+
const DEFAULT_EXPANDED_FOLDERS = ["notebooks/", "models/"];
|
|
13
|
+
|
|
14
|
+
interface ModelsProps {
|
|
15
|
+
server?: string;
|
|
16
|
+
packageName: string;
|
|
17
|
+
versionId?: string;
|
|
18
|
+
navigate: (to: string) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function Models({
|
|
22
|
+
server,
|
|
23
|
+
packageName,
|
|
24
|
+
versionId,
|
|
25
|
+
navigate,
|
|
26
|
+
}: ModelsProps) {
|
|
27
|
+
const { data, isSuccess, isError, error } = useQuery(
|
|
28
|
+
{
|
|
29
|
+
queryKey: ["models", server, packageName, versionId],
|
|
30
|
+
queryFn: () =>
|
|
31
|
+
modelsApi.listModels(packageName, versionId, {
|
|
32
|
+
baseURL: server,
|
|
33
|
+
withCredentials: true,
|
|
34
|
+
}),
|
|
35
|
+
retry: false,
|
|
36
|
+
},
|
|
37
|
+
queryClient,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<StyledCard variant="outlined" sx={{ padding: "10px", width: "100%" }}>
|
|
42
|
+
<StyledCardContent>
|
|
43
|
+
<Typography variant="overline" fontWeight="bold">
|
|
44
|
+
Models
|
|
45
|
+
</Typography>
|
|
46
|
+
<Divider />
|
|
47
|
+
<Box
|
|
48
|
+
sx={{
|
|
49
|
+
mt: "10px",
|
|
50
|
+
maxHeight: "300px",
|
|
51
|
+
overflowY: "auto",
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
{!isSuccess && !isError && (
|
|
55
|
+
<Typography variant="body2" sx={{ p: "20px", m: "auto" }}>
|
|
56
|
+
Fetching Models
|
|
57
|
+
</Typography>
|
|
58
|
+
)}
|
|
59
|
+
{isSuccess && data.data.length > 0 && (
|
|
60
|
+
<FileTreeView
|
|
61
|
+
items={data.data.sort((a, b) => {
|
|
62
|
+
return a.path.localeCompare(b.path);
|
|
63
|
+
})}
|
|
64
|
+
defaultExpandedItems={DEFAULT_EXPANDED_FOLDERS}
|
|
65
|
+
navigate={navigate}
|
|
66
|
+
/>
|
|
67
|
+
)}
|
|
68
|
+
{isError && (
|
|
69
|
+
<Typography variant="body2" sx={{ p: "10px", m: "auto" }}>
|
|
70
|
+
{`${packageName} > ${versionId} - ${error.message}`}
|
|
71
|
+
</Typography>
|
|
72
|
+
)}
|
|
73
|
+
</Box>
|
|
74
|
+
</StyledCardContent>
|
|
75
|
+
</StyledCard>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Divider, Grid2, Typography } from "@mui/material";
|
|
2
|
+
import { Notebook } from "../Notebook";
|
|
3
|
+
import { StyledCard, StyledCardContent, StyledCardMedia } from "../styles";
|
|
4
|
+
import Config from "./Config";
|
|
5
|
+
import Models from "./Models";
|
|
6
|
+
import Databases from "./Databases";
|
|
7
|
+
|
|
8
|
+
const README_NOTEBOOK = "README.malloynb";
|
|
9
|
+
|
|
10
|
+
interface PackageProps {
|
|
11
|
+
server?: string;
|
|
12
|
+
packageName: string;
|
|
13
|
+
versionId?: string;
|
|
14
|
+
navigate?: (to: string) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function Package({
|
|
18
|
+
server,
|
|
19
|
+
packageName,
|
|
20
|
+
versionId,
|
|
21
|
+
navigate,
|
|
22
|
+
}: PackageProps) {
|
|
23
|
+
if (!navigate) {
|
|
24
|
+
navigate = (to: string) => {
|
|
25
|
+
window.location.href = to;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Grid2
|
|
31
|
+
container
|
|
32
|
+
spacing={2}
|
|
33
|
+
columns={12}
|
|
34
|
+
sx={{ mb: (theme) => theme.spacing(2) }}
|
|
35
|
+
>
|
|
36
|
+
<Grid2 size={{ md: 12, lg: 4 }}>
|
|
37
|
+
<Config
|
|
38
|
+
server={server}
|
|
39
|
+
packageName={packageName}
|
|
40
|
+
versionId={versionId}
|
|
41
|
+
/>
|
|
42
|
+
</Grid2>
|
|
43
|
+
<Grid2 size={{ md: 12, lg: 4 }}>
|
|
44
|
+
<Models
|
|
45
|
+
server={server}
|
|
46
|
+
packageName={packageName}
|
|
47
|
+
versionId={versionId}
|
|
48
|
+
navigate={navigate}
|
|
49
|
+
/>
|
|
50
|
+
</Grid2>
|
|
51
|
+
<Grid2 size={{ md: 12, lg: 4 }}>
|
|
52
|
+
<Databases
|
|
53
|
+
server={server}
|
|
54
|
+
packageName={packageName}
|
|
55
|
+
versionId={versionId}
|
|
56
|
+
/>
|
|
57
|
+
</Grid2>
|
|
58
|
+
<Grid2 size={{ md: 12 }}>
|
|
59
|
+
<StyledCard variant="outlined">
|
|
60
|
+
<StyledCardContent>
|
|
61
|
+
<Typography variant="overline" fontWeight="bold">
|
|
62
|
+
Readme
|
|
63
|
+
</Typography>
|
|
64
|
+
<Divider />
|
|
65
|
+
</StyledCardContent>
|
|
66
|
+
<StyledCardMedia>
|
|
67
|
+
<Notebook
|
|
68
|
+
server={server}
|
|
69
|
+
packageName={packageName}
|
|
70
|
+
notebookPath={README_NOTEBOOK}
|
|
71
|
+
versionId={versionId}
|
|
72
|
+
/>
|
|
73
|
+
</StyledCardMedia>
|
|
74
|
+
</StyledCard>
|
|
75
|
+
</Grid2>
|
|
76
|
+
</Grid2>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Package } from "./Package";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Divider, Typography } from "@mui/material";
|
|
2
|
+
import { Configuration, DefaultApi } from "../../client";
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
import Markdown from "markdown-to-jsx";
|
|
5
|
+
import { StyledCard, StyledCardContent, StyledCardMedia } from "../styles";
|
|
6
|
+
import { QueryClient, useQuery } from "@tanstack/react-query";
|
|
7
|
+
|
|
8
|
+
axios.defaults.baseURL = "http://localhost:4000";
|
|
9
|
+
const aboutApi = new DefaultApi(new Configuration());
|
|
10
|
+
const queryClient = new QueryClient();
|
|
11
|
+
|
|
12
|
+
interface AboutProps {
|
|
13
|
+
server?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function About({ server }: AboutProps) {
|
|
17
|
+
const { data, isSuccess, isError, error } = useQuery(
|
|
18
|
+
{
|
|
19
|
+
queryKey: ["about", server],
|
|
20
|
+
queryFn: () =>
|
|
21
|
+
aboutApi.about({
|
|
22
|
+
baseURL: server,
|
|
23
|
+
withCredentials: true,
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
queryClient,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
{!isSuccess && !isError && (
|
|
32
|
+
<Typography variant="body2" sx={{ p: "10px", m: "auto" }}>
|
|
33
|
+
Fetching About...
|
|
34
|
+
</Typography>
|
|
35
|
+
)}
|
|
36
|
+
{isSuccess && (
|
|
37
|
+
<StyledCard variant="outlined">
|
|
38
|
+
<StyledCardContent>
|
|
39
|
+
<Typography variant="overline" fontWeight="bold">
|
|
40
|
+
Readme
|
|
41
|
+
</Typography>
|
|
42
|
+
<Divider />
|
|
43
|
+
</StyledCardContent>
|
|
44
|
+
<StyledCardMedia>
|
|
45
|
+
<Markdown>{data.data.readme}</Markdown>
|
|
46
|
+
</StyledCardMedia>
|
|
47
|
+
</StyledCard>
|
|
48
|
+
)}
|
|
49
|
+
{isError && (
|
|
50
|
+
<Typography variant="body2" sx={{ p: "10px", m: "auto" }}>
|
|
51
|
+
{error.message}
|
|
52
|
+
</Typography>
|
|
53
|
+
)}
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Box, Divider, Grid2, Typography } from "@mui/material";
|
|
2
|
+
import { Configuration, PackagesApi } from "../../client";
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
import { StyledCard, StyledCardContent, StyledCardMedia } from "../styles";
|
|
5
|
+
import { QueryClient, useQuery } from "@tanstack/react-query";
|
|
6
|
+
|
|
7
|
+
axios.defaults.baseURL = "http://localhost:4000";
|
|
8
|
+
const packagesApi = new PackagesApi(new Configuration());
|
|
9
|
+
const queryClient = new QueryClient();
|
|
10
|
+
|
|
11
|
+
interface ProjectProps {
|
|
12
|
+
server?: string;
|
|
13
|
+
navigate?: (to: string) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function Project({ server, navigate }: ProjectProps) {
|
|
17
|
+
const { data, isSuccess, isError, error } = useQuery(
|
|
18
|
+
{
|
|
19
|
+
queryKey: ["packages", server],
|
|
20
|
+
queryFn: () =>
|
|
21
|
+
packagesApi.listPackages({
|
|
22
|
+
baseURL: server,
|
|
23
|
+
withCredentials: true,
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
queryClient,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (!navigate) {
|
|
30
|
+
navigate = (to: string) => {
|
|
31
|
+
window.location.href = to;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
{!isSuccess && !isError && (
|
|
38
|
+
<Typography variant="body2" sx={{ p: "20px", m: "auto" }}>
|
|
39
|
+
Fetching Packages...
|
|
40
|
+
</Typography>
|
|
41
|
+
)}
|
|
42
|
+
{isSuccess && (
|
|
43
|
+
<StyledCard variant="outlined">
|
|
44
|
+
<StyledCardContent>
|
|
45
|
+
<Typography variant="overline" fontWeight="bold">
|
|
46
|
+
Packages
|
|
47
|
+
</Typography>
|
|
48
|
+
<Divider />
|
|
49
|
+
</StyledCardContent>
|
|
50
|
+
<StyledCardMedia>
|
|
51
|
+
<Grid2
|
|
52
|
+
container
|
|
53
|
+
spacing={2}
|
|
54
|
+
columns={12}
|
|
55
|
+
sx={{ mb: (theme) => theme.spacing(2) }}
|
|
56
|
+
>
|
|
57
|
+
{data.data
|
|
58
|
+
.sort((a, b) => {
|
|
59
|
+
return a.name.localeCompare(b.name);
|
|
60
|
+
})
|
|
61
|
+
.map((p) => (
|
|
62
|
+
<Grid2
|
|
63
|
+
size={{ xs: 12, sm: 12, md: 12, lg: 4 }}
|
|
64
|
+
key={p.name}
|
|
65
|
+
>
|
|
66
|
+
<StyledCard
|
|
67
|
+
variant="outlined"
|
|
68
|
+
sx={{ padding: "10px", cursor: "pointer" }}
|
|
69
|
+
onClick={() => navigate(p.name + "/")}
|
|
70
|
+
>
|
|
71
|
+
<StyledCardContent>
|
|
72
|
+
<Typography
|
|
73
|
+
variant="overline"
|
|
74
|
+
color="primary.main"
|
|
75
|
+
>
|
|
76
|
+
{p.name}
|
|
77
|
+
</Typography>
|
|
78
|
+
<Divider />
|
|
79
|
+
<Box
|
|
80
|
+
sx={{
|
|
81
|
+
mt: "10px",
|
|
82
|
+
maxHeight: "100px",
|
|
83
|
+
overflowY: "auto",
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<Typography variant="body2">
|
|
87
|
+
{p.description}
|
|
88
|
+
</Typography>
|
|
89
|
+
</Box>
|
|
90
|
+
</StyledCardContent>
|
|
91
|
+
</StyledCard>
|
|
92
|
+
</Grid2>
|
|
93
|
+
))}
|
|
94
|
+
</Grid2>
|
|
95
|
+
</StyledCardMedia>
|
|
96
|
+
</StyledCard>
|
|
97
|
+
)}
|
|
98
|
+
{isError && (
|
|
99
|
+
<Typography variant="body2" sx={{ p: "10px", m: "auto" }}>
|
|
100
|
+
{error.message}
|
|
101
|
+
</Typography>
|
|
102
|
+
)}
|
|
103
|
+
</>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Grid2 } from "@mui/material";
|
|
2
|
+
import About from "./About";
|
|
3
|
+
import Packages from "./Packages";
|
|
4
|
+
|
|
5
|
+
interface ProjectProps {
|
|
6
|
+
server?: string;
|
|
7
|
+
navigate?: (to: string) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function Project({ server, navigate }: ProjectProps) {
|
|
11
|
+
return (
|
|
12
|
+
<Grid2
|
|
13
|
+
container
|
|
14
|
+
spacing={2}
|
|
15
|
+
columns={12}
|
|
16
|
+
sx={{ mb: (theme) => theme.spacing(2) }}
|
|
17
|
+
>
|
|
18
|
+
<Grid2 size={{ xs: 12, md: 12 }}>
|
|
19
|
+
<Packages server={server} navigate={navigate} />
|
|
20
|
+
</Grid2>
|
|
21
|
+
<Grid2 size={{ xs: 12, md: 12 }}>
|
|
22
|
+
<About server={server} />
|
|
23
|
+
</Grid2>
|
|
24
|
+
</Grid2>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Project } from "./Project";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Suspense, lazy } from "react";
|
|
2
|
+
import { Configuration, QueryresultsApi } from "../../client";
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
import { Typography } from "@mui/material";
|
|
5
|
+
import { QueryClient, useQuery } from "@tanstack/react-query";
|
|
6
|
+
|
|
7
|
+
const RenderedResult = lazy(() => import("../RenderedResult/RenderedResult"));
|
|
8
|
+
|
|
9
|
+
axios.defaults.baseURL = "http://localhost:4000";
|
|
10
|
+
const queryResultsApi = new QueryresultsApi(new Configuration());
|
|
11
|
+
const queryClient = new QueryClient();
|
|
12
|
+
|
|
13
|
+
interface QueryResultProps {
|
|
14
|
+
server?: string;
|
|
15
|
+
packageName: string;
|
|
16
|
+
modelPath: string;
|
|
17
|
+
versionId?: string;
|
|
18
|
+
query?: string;
|
|
19
|
+
sourceName?: string;
|
|
20
|
+
queryName?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default function QueryResult({
|
|
24
|
+
server,
|
|
25
|
+
packageName,
|
|
26
|
+
modelPath,
|
|
27
|
+
versionId,
|
|
28
|
+
query,
|
|
29
|
+
sourceName,
|
|
30
|
+
queryName,
|
|
31
|
+
}: QueryResultProps) {
|
|
32
|
+
const { data, isSuccess, isError, error } = useQuery(
|
|
33
|
+
{
|
|
34
|
+
queryKey: [
|
|
35
|
+
"queryResult",
|
|
36
|
+
server,
|
|
37
|
+
packageName,
|
|
38
|
+
modelPath,
|
|
39
|
+
versionId,
|
|
40
|
+
query,
|
|
41
|
+
sourceName,
|
|
42
|
+
queryName,
|
|
43
|
+
],
|
|
44
|
+
queryFn: () =>
|
|
45
|
+
queryResultsApi.executeQuery(
|
|
46
|
+
packageName,
|
|
47
|
+
modelPath,
|
|
48
|
+
versionId,
|
|
49
|
+
query,
|
|
50
|
+
sourceName,
|
|
51
|
+
queryName,
|
|
52
|
+
{
|
|
53
|
+
baseURL: server,
|
|
54
|
+
withCredentials: true,
|
|
55
|
+
},
|
|
56
|
+
),
|
|
57
|
+
},
|
|
58
|
+
queryClient,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
{!isSuccess && !isError && (
|
|
64
|
+
<Typography variant="body2" sx={{ p: "20px", m: "auto" }}>
|
|
65
|
+
Fetching Query Results...
|
|
66
|
+
</Typography>
|
|
67
|
+
)}
|
|
68
|
+
{isSuccess && (
|
|
69
|
+
<Suspense fallback="Loading malloy...">
|
|
70
|
+
<RenderedResult
|
|
71
|
+
queryResult={data.data.queryResult}
|
|
72
|
+
modelDef={data.data.modelDef}
|
|
73
|
+
dataStyles={data.data.dataStyles}
|
|
74
|
+
/>
|
|
75
|
+
</Suspense>
|
|
76
|
+
)}
|
|
77
|
+
{isError && (
|
|
78
|
+
<Typography variant="body2" sx={{ p: "10px", m: "auto" }}>
|
|
79
|
+
{`${packageName} > ${modelPath} > ${versionId} - ${error.message}`}
|
|
80
|
+
</Typography>
|
|
81
|
+
)}
|
|
82
|
+
</>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as QueryResult } from "./QueryResult";
|