16 | {members.map((m) => (
17 |
18 | updateMethods.remove(m)}>
19 |
21 | updateMethods.update(m, { ...m, dimension: updateWith })
22 | }
23 | availableMembers={availableMembers}
24 | style={{
25 | width: 150,
26 | textOverflow: 'ellipsis',
27 | overflow: 'hidden',
28 | }}
29 | >
30 | {m.dimension.title}
31 |
32 |
33 |
44 |
49 |
50 | ))}
51 | updateMethods.add({ dimension: m })}
53 | availableMembers={availableMembers}
54 | type="dashed"
55 | icon={}
56 | >
57 | {addMemberName}
58 |
59 |
60 | );
61 |
62 | FilterGroup.propTypes = {
63 | members: PropTypes.array.isRequired,
64 | availableMembers: PropTypes.array.isRequired,
65 | addMemberName: PropTypes.string.isRequired,
66 | updateMethods: PropTypes.object.isRequired,
67 | };
68 |
69 | export default FilterGroup;
70 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/FilterInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as PropTypes from 'prop-types';
3 | import { Select, Input } from 'antd';
4 |
5 | const FilterInputs = {
6 | string: ({ values, onChange }) => (
7 | updateMethods.update(member, { ...member, values })}
50 | />
51 | );
52 | };
53 |
54 | FilterInput.propTypes = {
55 | member: PropTypes.object.isRequired,
56 | updateMethods: PropTypes.object.isRequired,
57 | };
58 |
59 | export default FilterInput;
60 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/MemberDropdown.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as PropTypes from 'prop-types';
3 | import { Menu } from 'antd';
4 | import ButtonDropdown from './ButtonDropdown';
5 |
6 | // Can't be a Pure Component due to Dropdown lookups overlay component type to set appropriate styles
7 | const memberMenu = (onClick, availableMembers) => (
8 |
19 | );
20 |
21 | const MemberDropdown = ({ onClick, availableMembers, ...buttonProps }) => (
22 |
26 | );
27 |
28 | MemberDropdown.propTypes = {
29 | onClick: PropTypes.func.isRequired,
30 | availableMembers: PropTypes.array.isRequired,
31 | };
32 |
33 | export default MemberDropdown;
34 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/MemberGroup.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as PropTypes from 'prop-types';
3 | import { Icon } from '@ant-design/compatible';
4 | import MemberDropdown from './MemberDropdown';
5 | import RemoveButtonGroup from './RemoveButtonGroup';
6 |
7 | const MemberGroup = ({
8 | members,
9 | availableMembers,
10 | addMemberName,
11 | updateMethods,
12 | }) => (
13 |
14 | {members.map((m) => (
15 | updateMethods.remove(m)}
18 | >
19 | updateMethods.update(m, updateWith)}
22 | >
23 | {m.title}
24 |
25 |
26 | ))}
27 | updateMethods.add(m)}
29 | availableMembers={availableMembers}
30 | type="dashed"
31 | icon={}
32 | >
33 | {addMemberName}
34 |
35 |
36 | );
37 |
38 | MemberGroup.propTypes = {
39 | members: PropTypes.array.isRequired,
40 | availableMembers: PropTypes.array.isRequired,
41 | addMemberName: PropTypes.string.isRequired,
42 | updateMethods: PropTypes.object.isRequired,
43 | };
44 |
45 | export default MemberGroup;
46 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/Order/DraggableItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button, Typography } from 'antd';
3 | import { Draggable } from 'react-beautiful-dnd';
4 | import { DragOutlined } from '@ant-design/icons';
5 |
6 | const orderOptions = ['asc', 'desc', 'none'];
7 |
8 | export default function DraggableItem({
9 | id,
10 | index,
11 | order = 'none',
12 | children,
13 | onOrderChange,
14 | }) {
15 | const getNextOrder = () => {
16 | const index = orderOptions.indexOf(order) + 1;
17 | return orderOptions[index > 2 ? 0 : index];
18 | };
19 |
20 | return (
21 |
22 | {({ draggableProps, dragHandleProps, innerRef }) => (
23 |
36 |
37 |
38 |
39 | {children}
40 |
41 |
42 |
53 |
54 | )}
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/Order/OrderGroup.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { DragDropContext, Droppable } from 'react-beautiful-dnd';
3 | import DraggableItem from './DraggableItem';
4 |
5 | export default function OrderGroup({ orderMembers, onOrderChange, onReorder }) {
6 | return (
7 | {
9 | onReorder(source && source.index, destination && destination.index);
10 | }}
11 | >
12 |
13 | {(provided) => (
14 |
22 | {orderMembers.map(({ id, title, order }, index) => (
23 |
30 | {title}
31 |
32 | ))}
33 |
34 | {provided.placeholder}
35 |
36 | )}
37 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/Pivot/Axes.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { DragDropContext } from 'react-beautiful-dnd';
3 | import { Row, Col, Divider } from 'antd';
4 | import DroppableArea from './DroppableArea';
5 |
6 | export default function Axes({ pivotConfig, onMove }) {
7 | const [uiPivotConfig, setUIPivotConfig] = useState(pivotConfig);
8 |
9 | useEffect(() => {
10 | setUIPivotConfig(pivotConfig);
11 | }, [pivotConfig]);
12 |
13 | return (
14 | {
16 | if (!destination) {
17 | return;
18 | }
19 | onMove({
20 | sourceIndex: source.index,
21 | destinationIndex: destination.index,
22 | sourceAxis: source.droppableId,
23 | destinationAxis: destination.droppableId,
24 | callback(updatedPivotConfig) {
25 | setUIPivotConfig(updatedPivotConfig);
26 | },
27 | });
28 | }}
29 | >
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/Pivot/DroppableArea.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Typography } from 'antd';
3 | import { Droppable } from 'react-beautiful-dnd';
4 | import Item from './Item';
5 |
6 | export default function DroppableArea({ pivotConfig, axis }) {
7 | return (
8 | <>
9 |
16 | {axis}
17 |
18 |
19 | {(provided) => (
20 |
25 | {pivotConfig[axis].map((id, index) => (
26 |
27 | ))}
28 |
29 | {provided.placeholder}
30 |
31 | )}
32 |
33 | >
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/Pivot/Item.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Draggable } from 'react-beautiful-dnd';
3 | import { DragOutlined } from '@ant-design/icons';
4 | import { Typography } from 'antd';
5 |
6 | export default function Item({ id, index }) {
7 | return (
8 |
9 | {({ draggableProps, dragHandleProps, innerRef }) => (
10 |
18 |
19 |
20 | {id}
21 |
22 |
23 | )}
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/Pivot/Options.js:
--------------------------------------------------------------------------------
1 | import { Checkbox } from 'antd';
2 | import React from 'react';
3 |
4 | export default function Options({ pivotConfig, onUpdate }) {
5 | return (
6 |
9 | onUpdate({
10 | fillMissingDates: !pivotConfig.fillMissingDates,
11 | })
12 | }
13 | >
14 | Fill Missing Dates
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/Pivot/Pivot.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Tabs } from 'antd';
3 | import Axes from './Axes';
4 | import Options from './Options';
5 |
6 | export default function Pivot({ pivotConfig, onMove, onUpdate }) {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/RemoveButtonGroup.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as PropTypes from 'prop-types';
3 | import { Button } from 'antd';
4 | import { Icon } from '@ant-design/compatible';
5 |
6 | const RemoveButtonGroup = ({ onRemoveClick, children, ...props }) => (
7 |
8 | {children}
9 |
12 |
13 | );
14 |
15 | RemoveButtonGroup.propTypes = {
16 | onRemoveClick: PropTypes.func.isRequired,
17 | children: PropTypes.object.isRequired,
18 | };
19 |
20 | export default RemoveButtonGroup;
21 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/QueryBuilder/SelectChartType.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as PropTypes from 'prop-types';
3 | import { Menu } from 'antd';
4 | import { Icon } from '@ant-design/compatible';
5 | import ButtonDropdown from './ButtonDropdown';
6 |
7 | const ChartTypes = [
8 | { name: 'line', title: 'Line', icon: 'line-chart' },
9 | { name: 'area', title: 'Area', icon: 'area-chart' },
10 | { name: 'bar', title: 'Bar', icon: 'bar-chart' },
11 | { name: 'pie', title: 'Pie', icon: 'pie-chart' },
12 | { name: 'table', title: 'Table', icon: 'table' },
13 | { name: 'number', title: 'Number', icon: 'info-circle' },
14 | ];
15 |
16 | const SelectChartType = ({ chartType, updateChartType }) => {
17 | const menu = (
18 |
26 | );
27 |
28 | const foundChartType = ChartTypes.find((t) => t.name === chartType);
29 | return (
30 | }>
31 | {foundChartType.title}
32 |
33 | );
34 | };
35 |
36 | SelectChartType.propTypes = {
37 | chartType: PropTypes.string.isRequired,
38 | updateChartType: PropTypes.func.isRequired,
39 | };
40 |
41 | export default SelectChartType;
42 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/components/TitleModal.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Modal, Input } from 'antd';
3 | import { useMutation } from '@apollo/react-hooks';
4 | import { GET_DASHBOARD_ITEMS } from '../graphql/queries';
5 | import {
6 | CREATE_DASHBOARD_ITEM,
7 | UPDATE_DASHBOARD_ITEM,
8 | } from '../graphql/mutations';
9 |
10 | const TitleModal = ({
11 | history,
12 | itemId,
13 | titleModalVisible,
14 | setTitleModalVisible,
15 | setAddingToDashboard,
16 | finalVizState,
17 | setTitle,
18 | finalTitle,
19 | }) => {
20 | const [addDashboardItem] = useMutation(CREATE_DASHBOARD_ITEM, {
21 | refetchQueries: [
22 | {
23 | query: GET_DASHBOARD_ITEMS,
24 | },
25 | ],
26 | });
27 | const [updateDashboardItem] = useMutation(UPDATE_DASHBOARD_ITEM, {
28 | refetchQueries: [
29 | {
30 | query: GET_DASHBOARD_ITEMS,
31 | },
32 | ],
33 | });
34 |
35 | return (
36 | {
41 | setTitleModalVisible(false);
42 | setAddingToDashboard(true);
43 |
44 | try {
45 | await (itemId ? updateDashboardItem : addDashboardItem)({
46 | variables: {
47 | id: itemId,
48 | input: {
49 | vizState: JSON.stringify(finalVizState),
50 | name: finalTitle,
51 | },
52 | },
53 | });
54 | history.push('/');
55 | } finally {
56 | setAddingToDashboard(false);
57 | }
58 | }}
59 | onCancel={() => setTitleModalVisible(false)}
60 | >
61 | setTitle(e.target.value)}
65 | />
66 |
67 | );
68 | };
69 |
70 | export default TitleModal;
71 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/graphql/client.js:
--------------------------------------------------------------------------------
1 | /* globals window */
2 | import { ApolloClient } from 'apollo-client';
3 | import { InMemoryCache } from 'apollo-cache-inmemory';
4 | import { SchemaLink } from 'apollo-link-schema';
5 | import { makeExecutableSchema } from 'graphql-tools';
6 |
7 | const cache = new InMemoryCache();
8 | const defaultDashboardItems = [];
9 |
10 | const getDashboardItems = () =>
11 | JSON.parse(window.localStorage.getItem('dashboardItems')) ||
12 | defaultDashboardItems;
13 |
14 | const setDashboardItems = (items) =>
15 | window.localStorage.setItem('dashboardItems', JSON.stringify(items));
16 |
17 | const nextId = () => {
18 | const currentId =
19 | parseInt(window.localStorage.getItem('dashboardIdCounter'), 10) || 1;
20 | window.localStorage.setItem('dashboardIdCounter', currentId + 1);
21 | return currentId.toString();
22 | };
23 |
24 | const toApolloItem = (i) => ({
25 | ...i,
26 | __typename: 'DashboardItem',
27 | });
28 |
29 | const typeDefs = `
30 | type DashboardItem {
31 | id: String!
32 | layout: String
33 | vizState: String
34 | name: String
35 | }
36 |
37 | input DashboardItemInput {
38 | layout: String
39 | vizState: String
40 | name: String
41 | }
42 |
43 | type Query {
44 | dashboardItems: [DashboardItem]
45 | dashboardItem(id: String!): DashboardItem
46 | }
47 |
48 | type Mutation {
49 | createDashboardItem(input: DashboardItemInput): DashboardItem
50 | updateDashboardItem(id: String!, input: DashboardItemInput): DashboardItem
51 | deleteDashboardItem(id: String!): DashboardItem
52 | }
53 | `;
54 |
55 | const schema = makeExecutableSchema({
56 | typeDefs,
57 | resolvers: {
58 | Query: {
59 | dashboardItems() {
60 | const dashboardItems = getDashboardItems();
61 | return dashboardItems.map(toApolloItem);
62 | },
63 | dashboardItem(_, { id }) {
64 | const dashboardItems = getDashboardItems();
65 | return toApolloItem(dashboardItems.find((i) => i.id.toString() === id));
66 | },
67 | },
68 | Mutation: {
69 | createDashboardItem: (_, { input: { ...item } }) => {
70 | const dashboardItems = getDashboardItems();
71 | item = { ...item, id: nextId(), layout: JSON.stringify({}) };
72 | dashboardItems.push(item);
73 | setDashboardItems(dashboardItems);
74 | return toApolloItem(item);
75 | },
76 | updateDashboardItem: (_, { id, input: { ...item } }) => {
77 | const dashboardItems = getDashboardItems();
78 | item = Object.keys(item)
79 | .filter((k) => !!item[k])
80 | .map((k) => ({
81 | [k]: item[k],
82 | }))
83 | .reduce((a, b) => ({ ...a, ...b }), {});
84 | const index = dashboardItems.findIndex((i) => i.id.toString() === id);
85 | dashboardItems[index] = { ...dashboardItems[index], ...item };
86 | setDashboardItems(dashboardItems);
87 | return toApolloItem(dashboardItems[index]);
88 | },
89 | deleteDashboardItem: (_, { id }) => {
90 | const dashboardItems = getDashboardItems();
91 | const index = dashboardItems.findIndex((i) => i.id.toString() === id);
92 | const [removedItem] = dashboardItems.splice(index, 1);
93 | setDashboardItems(dashboardItems);
94 | return toApolloItem(removedItem);
95 | },
96 | },
97 | },
98 | });
99 |
100 | export default new ApolloClient({
101 | cache,
102 | link: new SchemaLink({ schema }),
103 | });
104 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/graphql/mutations.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const CREATE_DASHBOARD_ITEM = gql`
4 | mutation CreateDashboardItem($input: DashboardItemInput) {
5 | createDashboardItem(input: $input) {
6 | id
7 | layout
8 | vizState
9 | name
10 | }
11 | }
12 | `;
13 |
14 | export const UPDATE_DASHBOARD_ITEM = gql`
15 | mutation UpdateDashboardItem($id: String!, $input: DashboardItemInput) {
16 | updateDashboardItem(id: $id, input: $input) {
17 | id
18 | layout
19 | vizState
20 | name
21 | }
22 | }
23 | `;
24 |
25 | export const DELETE_DASHBOARD_ITEM = gql`
26 | mutation DeleteDashboardItem($id: String!) {
27 | deleteDashboardItem(id: $id) {
28 | id
29 | layout
30 | vizState
31 | name
32 | }
33 | }
34 | `;
35 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/graphql/queries.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const GET_DASHBOARD_ITEMS = gql`
4 | query GetDashboardItems {
5 | dashboardItems {
6 | id
7 | layout
8 | vizState
9 | name
10 | }
11 | }
12 | `;
13 |
14 | export const GET_DASHBOARD_ITEM = gql`
15 | query GetDashboardItem($id: String!) {
16 | dashboardItem(id: $id) {
17 | id
18 | layout
19 | vizState
20 | name
21 | }
22 | }
23 | `;
24 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { HashRouter as Router, Route } from 'react-router-dom';
4 | import ExplorePage from './pages/ExplorePage';
5 | import DashboardPage from './pages/DashboardPage';
6 | import App from './App';
7 |
8 | ReactDOM.render(
9 |
10 |
11 |
12 |
13 |
14 | ,
15 | // eslint-disable-next-line no-undef
16 | document.getElementById('root')
17 | );
18 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/pages/DashboardPage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Spin, Button, Alert } from 'antd';
3 | import { Link } from 'react-router-dom';
4 | import { useQuery } from '@apollo/react-hooks';
5 | import { Icon } from '@ant-design/compatible';
6 | import { GET_DASHBOARD_ITEMS } from '../graphql/queries';
7 | import ChartRenderer from '../components/ChartRenderer';
8 | import Dashboard from '../components/Dashboard';
9 | import DashboardItem from '../components/DashboardItem';
10 |
11 | const deserializeItem = (i) => ({
12 | ...i,
13 | layout: JSON.parse(i.layout) || {},
14 | vizState: JSON.parse(i.vizState),
15 | });
16 |
17 | const defaultLayout = (i) => ({
18 | x: i.layout.x || 0,
19 | y: i.layout.y || 0,
20 | w: i.layout.w || 4,
21 | h: i.layout.h || 8,
22 | minW: 4,
23 | minH: 8,
24 | });
25 |
26 | const DashboardPage = () => {
27 | const { loading, error, data } = useQuery(GET_DASHBOARD_ITEMS);
28 |
29 | if (loading) {
30 | return ;
31 | }
32 |
33 | if (error) {
34 | return (
35 |
40 | );
41 | }
42 |
43 | const dashboardItem = (item) => (
44 |
45 |
46 |
47 |
48 |
49 | );
50 |
51 | const Empty = () => (
52 |
58 |
There are no charts on this dashboard
59 |
60 | }>
61 | Add chart
62 |
63 |
64 |
65 | );
66 |
67 | return !data || data.dashboardItems.length ? (
68 |
69 | {data && data.dashboardItems.map(deserializeItem).map(dashboardItem)}
70 |
71 | ) : (
72 |
73 | );
74 | };
75 |
76 | export default DashboardPage;
77 |
--------------------------------------------------------------------------------
/packages/react-antd-dynamic/scaffolding/src/pages/ExplorePage.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Alert, Button, Spin } from 'antd';
3 | import { useQuery } from '@apollo/react-hooks';
4 | import { withRouter } from 'react-router-dom';
5 | import ExploreQueryBuilder from '../components/QueryBuilder/ExploreQueryBuilder';
6 | import { GET_DASHBOARD_ITEM } from '../graphql/queries';
7 | import TitleModal from '../components/TitleModal.js';
8 |
9 | const ExplorePage = withRouter(({ history, location }) => {
10 | const [addingToDashboard, setAddingToDashboard] = useState(false);
11 | const params = new URLSearchParams(location.search);
12 | const itemId = params.get('itemId');
13 | const { loading, error, data } = useQuery(GET_DASHBOARD_ITEM, {
14 | variables: {
15 | id: itemId,
16 | },
17 | skip: !itemId,
18 | });
19 |
20 | const [vizState, setVizState] = useState(null);
21 | const finalVizState =
22 | vizState ||
23 | (itemId && !loading && data && JSON.parse(data.dashboardItem.vizState)) ||
24 | {};
25 | const [titleModalVisible, setTitleModalVisible] = useState(false);
26 | const [title, setTitle] = useState(null);
27 | const finalTitle =
28 | title != null
29 | ? title
30 | : (itemId && !loading && data && data.dashboardItem.name) || 'New Chart';
31 |
32 | if (loading) {
33 | return ;
34 | }
35 |
36 | if (error) {
37 | return ;
38 | }
39 |
40 | return (
41 |
42 |
52 | setTitleModalVisible(true)}
60 | >
61 | {itemId ? 'Update' : 'Add to Dashboard'}
62 | ,
63 | ]}
64 | onVizStateChanged={setVizState}
65 | />
66 |
67 | );
68 | });
69 | export default ExplorePage;
70 |
--------------------------------------------------------------------------------
/packages/react-antd-static/index.js:
--------------------------------------------------------------------------------
1 | const {
2 | TemplatePackage,
3 | AppSnippet,
4 | IndexSnippet,
5 | } = require('@cubejs-templates/core');
6 |
7 | class ReactAntdStaticTemplate extends TemplatePackage {
8 | importDependencies() {
9 | return {
10 | 'react-router': '5.2.1',
11 | 'react-router-dom': '5.2.1',
12 | };
13 | }
14 | }
15 |
16 | module.exports = (context) =>
17 | new ReactAntdStaticTemplate(context, {
18 | '/src/App.js': new AppSnippet(),
19 | '/src/index.js': new IndexSnippet(),
20 | });
21 |
--------------------------------------------------------------------------------
/packages/react-antd-static/scaffolding/src/App.js:
--------------------------------------------------------------------------------
1 | import './body.css';
2 | import 'antd/dist/antd.css';
3 | import React from 'react';
4 | import '@ant-design/compatible';
5 | import { Layout } from 'antd';
6 | import cubejs from '@cubejs-client/core';
7 | import { CubeProvider } from '@cubejs-client/react';
8 | import Header from './components/Header';
9 |
10 | const API_URL = undefined;
11 |
12 | const CUBEJS_TOKEN = undefined;
13 |
14 | const cubejsApi = cubejs(CUBEJS_TOKEN, { apiUrl: `${API_URL}/cubejs-api/v1` });
15 |
16 | const AppLayout = ({ children }) => (
17 |
22 |
23 | {children}
24 |
25 | );
26 |
27 | const App = ({ children }) => (
28 |
29 | {children}
30 |
31 | );
32 |
33 | export default App;
34 |
--------------------------------------------------------------------------------
/packages/react-antd-static/scaffolding/src/body.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #f0f2f5 !important;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/react-antd-static/scaffolding/src/components/ChartRenderer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { useCubeQuery } from '@cubejs-client/react';
4 | import { Spin } from 'antd';
5 |
6 | const TypeToChartComponent = {};
7 |
8 | const TypeToMemoChartComponent = Object.keys(TypeToChartComponent)
9 | .map((key) => ({ [key]: React.memo(TypeToChartComponent[key]) }))
10 | .reduce((a, b) => ({ ...a, ...b }));
11 |
12 | const renderChart = (Component) => ({ resultSet, error }) =>
13 | (resultSet && ) ||
14 | (error && error.toString()) || ;
15 |
16 | const ChartRenderer = ({ vizState }) => {
17 | const { query, chartType } = vizState;
18 | const component = TypeToMemoChartComponent[chartType];
19 | const renderProps = useCubeQuery(query);
20 |
21 | return component && renderChart(component)(renderProps);
22 | };
23 |
24 | ChartRenderer.propTypes = {
25 | vizState: PropTypes.object,
26 | cubejsApi: PropTypes.object,
27 | };
28 |
29 | ChartRenderer.defaultProps = {
30 | vizState: {},
31 | cubejsApi: null,
32 | };
33 |
34 | export default ChartRenderer;
35 |
--------------------------------------------------------------------------------
/packages/react-antd-static/scaffolding/src/components/Dashboard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Row } from 'antd';
3 |
4 | const Dashboard = ({ children }) => (
5 |
15 | {children}
16 |
17 | );
18 |
19 | export default Dashboard;
20 |
--------------------------------------------------------------------------------
/packages/react-antd-static/scaffolding/src/components/DashboardItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Card } from 'antd';
3 |
4 | const DashboardItem = ({ children, title }) => (
5 |
12 | {children}
13 |
14 | );
15 |
16 | export default DashboardItem;
17 |
--------------------------------------------------------------------------------
/packages/react-antd-static/scaffolding/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 | import { withRouter } from 'react-router';
4 | import { Layout, Menu } from 'antd';
5 |
6 | const Header = ({ location }) => (
7 |
12 |
17 |
27 | My Dashboard
28 |
29 |
30 |
42 |
43 | );
44 |
45 | export default withRouter(Header);
46 |
--------------------------------------------------------------------------------
/packages/react-antd-static/scaffolding/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { HashRouter as Router, Route } from 'react-router-dom';
4 | import DashboardPage from './pages/DashboardPage';
5 | import App from './App';
6 |
7 | ReactDOM.render(
8 |
9 |
10 |
11 |
12 | ,
13 | // eslint-disable-next-line no-undef
14 | document.getElementById('root')
15 | );
16 |
--------------------------------------------------------------------------------
/packages/react-antd-static/scaffolding/src/pages/DashboardPage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Col } from 'antd';
3 | import ChartRenderer from '../components/ChartRenderer';
4 | import Dashboard from '../components/Dashboard';
5 | import DashboardItem from '../components/DashboardItem';
6 |
7 | const DashboardItems = [];
8 |
9 | const DashboardPage = () => {
10 | const dashboardItem = (item) => (
11 |
12 |
13 |
14 |
15 |
16 | );
17 |
18 | const Empty = () => (
19 |
25 |
26 | There are no charts on this dashboard. Use Playground Build to add one.
27 |
28 |
29 | );
30 |
31 | return DashboardItems.length ? (
32 |
33 | {DashboardItems.map(dashboardItem)}
34 |
35 | ) : (
36 |
37 | );
38 | };
39 |
40 | export default DashboardPage;
41 |
--------------------------------------------------------------------------------
/packages/react-charting-library/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage } = require('@cubejs-templates/core');
2 |
3 | class ReactChartingLibraryTemplate extends TemplatePackage {}
4 |
5 | module.exports = (context) => new ReactChartingLibraryTemplate(context);
6 |
--------------------------------------------------------------------------------
/packages/react-charting-library/scaffolding/src/components/ChartRenderer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const TypeToChartComponent = {};
4 |
5 | export default TypeToChartComponent;
6 |
--------------------------------------------------------------------------------
/packages/react-charts/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage, AppSnippet } = require('@cubejs-templates/core');
2 |
3 | class ReactChartsTemplate extends TemplatePackage {
4 | importDependencies() {
5 | return {
6 | 'react-chartjs-2': '^3.0.3',
7 | 'chart.js': '^3.4.0',
8 | };
9 | }
10 | }
11 |
12 | module.exports = (context) =>
13 | new ReactChartsTemplate(context, {
14 | '/src/App.js': new AppSnippet(),
15 | });
16 |
--------------------------------------------------------------------------------
/packages/react-charts/scaffolding/src/App.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useLayoutEffect, useMemo } from 'react';
2 | import cubejs from '@cubejs-client/core';
3 | import { CubeProvider } from '@cubejs-client/react';
4 | import 'antd/dist/antd.css';
5 | import '@ant-design/compatible';
6 | import './style.css';
7 | import { getCodesandboxFiles, getDependencies } from './codegen';
8 | import ChartContainer from './ChartContainer';
9 |
10 | window['__cubejsPlayground'] = {
11 | getCodesandboxFiles,
12 | getDependencies,
13 | };
14 |
15 | const libs = {};
16 |
17 | const App = () => {
18 | const [_, queryId] = window.location.hash.replace(/#\\/, '').split('=');
19 |
20 | const [query, setQuery] = useState(null);
21 | const [pivotConfig, setPivotConfig] = useState(null);
22 | const [library, setLibrary] = useState(null);
23 | const [chartType, setChartType] = useState(null);
24 | const [credetialsVersion, updateVersion] = useState(0);
25 | const [refetchCounter, updateRefetchCounter] = useState(0);
26 |
27 | const cubejsApi = useMemo(() => {
28 | const data = window.parent.window['__cubejsPlayground'] || {};
29 |
30 | return cubejs(data.token, {
31 | apiUrl: data.apiUrl,
32 | });
33 | }, [credetialsVersion]);
34 |
35 | useEffect(() => {
36 | const { forQuery } = window.parent.window['__cubejsPlayground'] || {};
37 |
38 | if (typeof forQuery === 'function') {
39 | forQuery(queryId).onChartRendererReady();
40 | }
41 | }, []);
42 |
43 | useLayoutEffect(() => {
44 | window.addEventListener('__cubejsPlaygroundEvent', (event) => {
45 | const {
46 | query,
47 | chartingLibrary,
48 | chartType,
49 | pivotConfig,
50 | eventType,
51 | } = event.detail;
52 |
53 | if (eventType === 'chart') {
54 | if (query) {
55 | setQuery(query);
56 | }
57 | if (pivotConfig) {
58 | setPivotConfig(pivotConfig);
59 | }
60 | if (chartingLibrary) {
61 | setLibrary(chartingLibrary);
62 | }
63 | if (chartType) {
64 | if (chartingLibrary === 'bizcharts') {
65 | // avoid the bug where bizchars would throw an error on chart type change
66 | setChartType(null);
67 | setTimeout(() => setChartType(chartType), 0);
68 | } else {
69 | setChartType(chartType);
70 | }
71 | }
72 | } else if (eventType === 'credentials') {
73 | updateVersion((prev) => prev + 1);
74 | } else if (eventType === 'refetch') {
75 | updateRefetchCounter((prev) => prev + 1);
76 | }
77 | });
78 | }, []);
79 |
80 | return (
81 |
82 |
83 | {libs[library]?.[chartType] ? (
84 |
91 | ) : null}
92 |
93 |
94 | );
95 | };
96 |
97 | export default App;
98 |
--------------------------------------------------------------------------------
/packages/react-charts/scaffolding/src/ChartContainer.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useCubeQuery } from '@cubejs-client/react';
3 | import { isQueryPresent } from '@cubejs-client/core';
4 | import { useDeepCompareEffect } from 'use-deep-compare';
5 |
6 | const ChartRenderer = ({
7 | queryId,
8 | renderFunction,
9 | query,
10 | pivotConfig,
11 | refetchCounter,
12 | }) => {
13 | const { forQuery } = window.parent.window['__cubejsPlayground'] || {};
14 |
15 | const {
16 | onQueryStart,
17 | onQueryLoad,
18 | onQueryProgress,
19 | onQueryDrilldown,
20 | } = forQuery(queryId);
21 |
22 | const { isLoading, error, resultSet, progress, refetch } = useCubeQuery(
23 | query
24 | );
25 |
26 | const handleQueryDrilldownRequest = ({ xValues, yValues }, pivotConfig) => {
27 | if (typeof onQueryDrilldown === 'function') {
28 | onQueryDrilldown(
29 | resultSet.drillDown({
30 | xValues,
31 | yValues,
32 | }),
33 | pivotConfig
34 | );
35 | }
36 | };
37 |
38 | useDeepCompareEffect(() => {
39 | if (
40 | isLoading &&
41 | isQueryPresent(query) &&
42 | typeof onQueryStart === 'function'
43 | ) {
44 | onQueryStart(queryId);
45 | }
46 | }, [isLoading, query]);
47 |
48 | useEffect(() => {
49 | if (refetchCounter > 0) {
50 | refetch();
51 | }
52 | }, [refetchCounter]);
53 |
54 | useEffect(() => {
55 | if (!isLoading && typeof onQueryLoad === 'function') {
56 | onQueryLoad({
57 | resultSet,
58 | error,
59 | });
60 | }
61 |
62 | if (typeof onQueryProgress === 'function') {
63 | onQueryProgress(progress);
64 | }
65 | }, [error, isLoading, resultSet, progress]);
66 |
67 | if (!resultSet || error) {
68 | return null;
69 | }
70 |
71 | return renderFunction({
72 | resultSet,
73 | pivotConfig,
74 | onDrilldownRequested: handleQueryDrilldownRequest,
75 | });
76 | };
77 |
78 | const ChartContainer = ({
79 | queryId,
80 | renderFunction,
81 | query,
82 | pivotConfig = null,
83 | refetchCounter,
84 | }) => (
85 |
92 | );
93 |
94 | export default ChartContainer;
95 |
--------------------------------------------------------------------------------
/packages/react-charts/scaffolding/src/codegen.js:
--------------------------------------------------------------------------------
1 | import * as bizchartsCharts from './bizcharts-charts/src/code-chunks';
2 | import * as rechartsCharts from './recharts-charts/src/code-chunks';
3 | import * as chartjsCharts from './chartjs-charts/src/code-chunks';
4 | import * as d3Charts from './d3-charts/src/code-chunks';
5 |
6 | const chunksByLibrary = {
7 | bizchartsCharts,
8 | rechartsCharts,
9 | chartjsCharts,
10 | d3Charts,
11 | };
12 |
13 | const commonDependencies = [
14 | 'react-dom',
15 | '@cubejs-client/core',
16 | '@cubejs-client/react',
17 | ['antd', '4.16.13'],
18 | ];
19 |
20 | export function getCodesandboxFiles(
21 | chartingLibrary,
22 | { query, pivotConfig, chartType, cubejsToken, apiUrl }
23 | ) {
24 | const { getCommon, getImports, getChartComponent } = chunksByLibrary[
25 | `${chartingLibrary}Charts`
26 | ];
27 |
28 | return {
29 | 'index.js': `import ReactDOM from 'react-dom';
30 | import cubejs from '@cubejs-client/core';
31 | import { QueryRenderer } from '@cubejs-client/react';
32 | import { Spin } from 'antd';
33 | import 'antd/dist/antd.css';
34 | ${getImports().join('\n')}
35 |
36 | ${getCommon()}
37 |
38 | const cubejsApi = cubejs(
39 | '${cubejsToken}',
40 | { apiUrl: '${apiUrl}' }
41 | );
42 |
43 | const renderChart = ({ resultSet, error, pivotConfig, onDrilldownRequested }) => {
44 | if (error) {
45 | return {error.toString()}
;
46 | }
47 |
48 | if (!resultSet) {
49 | return ;
50 | }
51 |
52 | ${getChartComponent(chartType)}
53 | };
54 |
55 | const ChartRenderer = () => {
56 | return (
57 | renderChart({
62 | ...props,
63 | chartType: '${chartType}',
64 | pivotConfig: ${pivotConfig}
65 | })}
66 | />
67 | );
68 | };
69 |
70 | const rootElement = document.getElementById('root');
71 | ReactDOM.render(, rootElement);
72 | `,
73 | };
74 | }
75 |
76 | export function getDependencies(chartingLibrary) {
77 | if (!chartingLibrary) {
78 | throw new Error('`chartingLibrary` param is undefined');
79 | }
80 |
81 | const { getImports } = chunksByLibrary[`${chartingLibrary}Charts`];
82 |
83 | return [
84 | ...commonDependencies,
85 | ...getImports().map((i) => {
86 | const [, pkg] = i.match(/['"]([^'"]+)['"]/);
87 | return pkg;
88 | }),
89 | ];
90 | }
91 |
--------------------------------------------------------------------------------
/packages/react-charts/scaffolding/src/style.css:
--------------------------------------------------------------------------------
1 | #root,
2 | .App {
3 | height: 100%;
4 | }
5 |
--------------------------------------------------------------------------------
/packages/react-credentials/index.js:
--------------------------------------------------------------------------------
1 | const {
2 | TemplatePackage,
3 | CredentialsSnippet,
4 | } = require('@cubejs-templates/core');
5 |
6 | class AppCredentialsTemplate extends TemplatePackage {}
7 |
8 | module.exports = (context) => {
9 | if (!context.playgroundContext) {
10 | throw new Error('"playgroundContext" is required');
11 | }
12 |
13 | return new AppCredentialsTemplate(context, {
14 | '/src/App.js': new CredentialsSnippet(
15 | context.playgroundContext.credentials
16 | ),
17 | });
18 | };
19 |
--------------------------------------------------------------------------------
/packages/react-credentials/scaffolding/src/App.js:
--------------------------------------------------------------------------------
1 | const API_URL = undefined;
2 | const CUBEJS_TOKEN = undefined;
3 |
--------------------------------------------------------------------------------
/packages/react-material-static/index.js:
--------------------------------------------------------------------------------
1 | const {
2 | TemplatePackage,
3 | AppSnippet,
4 | IndexSnippet,
5 | } = require('@cubejs-templates/core');
6 |
7 | class ReactMaterialStaticTemplate extends TemplatePackage {
8 | importDependencies() {
9 | return {
10 | 'react-router': '5.2.1',
11 | 'react-router-dom': '5.2.1',
12 | };
13 | }
14 | }
15 |
16 | module.exports = (context) =>
17 | new ReactMaterialStaticTemplate(context, {
18 | '/src/App.js': new AppSnippet(),
19 | '/src/index.js': new IndexSnippet(),
20 | });
21 |
--------------------------------------------------------------------------------
/packages/react-material-static/scaffolding/src/App.js:
--------------------------------------------------------------------------------
1 | import './body.css';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import React from 'react';
4 | import cubejs from '@cubejs-client/core';
5 | import { CubeProvider } from '@cubejs-client/react';
6 | import Header from './components/Header';
7 |
8 | const API_URL = undefined;
9 |
10 | const CUBEJS_TOKEN = undefined;
11 |
12 | const cubejsApi = cubejs(CUBEJS_TOKEN, { apiUrl: `${API_URL}/cubejs-api/v1` });
13 |
14 | const useStyles = makeStyles(() => ({
15 | root: {
16 | flexGrow: 1,
17 | },
18 | }));
19 |
20 | const AppLayout = ({ children }) => {
21 | const classes = useStyles();
22 |
23 | return (
24 |
25 |
26 |
{children}
27 |
28 | );
29 | };
30 |
31 | const App = ({ children }) => (
32 |
33 | {children}
34 |
35 | );
36 |
37 | export default App;
38 |
--------------------------------------------------------------------------------
/packages/react-material-static/scaffolding/src/body.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #f0f2f5 !important;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/react-material-static/scaffolding/src/components/ChartRenderer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useCubeQuery } from '@cubejs-client/react';
3 | import CircularProgress from '@material-ui/core/CircularProgress';
4 |
5 | const TypeToChartComponent = {};
6 |
7 | const TypeToMemoChartComponent = Object.keys(TypeToChartComponent)
8 | .map((key) => ({ [key]: React.memo(TypeToChartComponent[key]) }))
9 | .reduce((a, b) => ({ ...a, ...b }));
10 |
11 | const renderChart = (Component) => ({ resultSet, error, ...props }) =>
12 | (resultSet && ) ||
13 | (error && error.toString()) || ;
14 |
15 | const ChartRenderer = ({ vizState = {} }) => {
16 | const { query, chartType, ...options } = vizState;
17 | const component = TypeToMemoChartComponent[chartType];
18 | const renderProps = useCubeQuery(query);
19 |
20 | return component && renderChart(component)({ ...options, ...renderProps });
21 | };
22 |
23 | export default ChartRenderer;
24 |
--------------------------------------------------------------------------------
/packages/react-material-static/scaffolding/src/components/Dashboard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Grid from '@material-ui/core/Grid';
3 |
4 | const Dashboard = ({ children }) => (
5 |
14 | {children}
15 |
16 | );
17 |
18 | export default Dashboard;
19 |
--------------------------------------------------------------------------------
/packages/react-material-static/scaffolding/src/components/DashboardItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Card from '@material-ui/core/Card';
3 | import CardContent from '@material-ui/core/CardContent';
4 | import Typography from '@material-ui/core/Typography';
5 |
6 | const DashboardItem = ({ children, title }) => (
7 |
8 |
9 | {title && (
10 |
11 | {title}
12 |
13 | )}
14 | {children}
15 |
16 |
17 | );
18 |
19 | export default DashboardItem;
20 |
--------------------------------------------------------------------------------
/packages/react-material-static/scaffolding/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withRouter } from 'react-router';
3 | import AppBar from '@material-ui/core/AppBar';
4 | import Toolbar from '@material-ui/core/Toolbar';
5 | import Typography from '@material-ui/core/Typography';
6 | import IconButton from '@material-ui/core/IconButton';
7 | import MenuIcon from '@material-ui/icons/Menu';
8 |
9 | const Header = () => (
10 |
11 |
12 |
13 |
14 |
15 |
16 | My Dashboard
17 |
18 |
19 |
20 | );
21 |
22 | export default withRouter(Header);
23 |
--------------------------------------------------------------------------------
/packages/react-material-static/scaffolding/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { HashRouter as Router, Route } from 'react-router-dom';
4 | import DashboardPage from './pages/DashboardPage';
5 | import App from './App';
6 |
7 | ReactDOM.render(
8 |
9 |
10 |
11 |
12 | ,
13 | // eslint-disable-next-line no-undef
14 | document.getElementById('root')
15 | );
16 |
--------------------------------------------------------------------------------
/packages/react-material-static/scaffolding/src/pages/DashboardPage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Grid from '@material-ui/core/Grid';
3 | import Typography from '@material-ui/core/Typography';
4 | import ChartRenderer from '../components/ChartRenderer';
5 | import Dashboard from '../components/Dashboard';
6 | import DashboardItem from '../components/DashboardItem';
7 |
8 | const DashboardItems = [];
9 |
10 | const DashboardPage = () => {
11 | const dashboardItem = (item) => (
12 |
13 |
14 |
15 |
16 |
17 | );
18 |
19 | const Empty = () => (
20 |
26 |
27 | There are no charts on this dashboard. Use Playground Build to add one.
28 |
29 |
30 | );
31 |
32 | return DashboardItems.length ? (
33 | {DashboardItems.map(dashboardItem)}
34 | ) : (
35 |
36 | );
37 | };
38 |
39 | export default DashboardPage;
40 |
--------------------------------------------------------------------------------
/packages/react-web-socket-transport/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage } = require('@cubejs-templates/core');
2 |
3 | class WebSocketTransportTemplate extends TemplatePackage {}
4 |
5 | module.exports = (context) => new WebSocketTransportTemplate(context);
6 |
--------------------------------------------------------------------------------
/packages/react-web-socket-transport/scaffolding/src/App.0.js:
--------------------------------------------------------------------------------
1 | const cubejsApi = cubejs(CUBEJS_TOKEN, {
2 | apiUrl: `${API_URL}/cubejs-api/v1`,
3 | });
4 |
--------------------------------------------------------------------------------
/packages/react-web-socket-transport/scaffolding/src/App.js:
--------------------------------------------------------------------------------
1 | import WebSocketTransport from '@cubejs-client/ws-transport';
2 |
3 | const cubejsApi = cubejs({
4 | transport: new WebSocketTransport({
5 | authorization: CUBEJS_TOKEN,
6 | apiUrl: API_URL.replace('http', 'ws'),
7 | }),
8 | });
9 |
--------------------------------------------------------------------------------
/packages/recharts-charts/index.js:
--------------------------------------------------------------------------------
1 | const {
2 | TemplatePackage,
3 | ChartRendererSnippet,
4 | } = require('@cubejs-templates/core');
5 |
6 | class RechartsChartsTemplate extends TemplatePackage {
7 | importDependencies() {
8 | return {
9 | recharts: '2.0.0-beta.8',
10 | };
11 | }
12 | }
13 |
14 | module.exports = (context) =>
15 | new RechartsChartsTemplate(context, {
16 | '/src/components/ChartRenderer.js': new ChartRendererSnippet(),
17 | });
18 |
--------------------------------------------------------------------------------
/packages/recharts-charts/scaffolding/src/components/ChartRenderer.js:
--------------------------------------------------------------------------------
1 | import {
2 | CartesianGrid,
3 | PieChart,
4 | Pie,
5 | Cell,
6 | AreaChart,
7 | Area,
8 | XAxis,
9 | YAxis,
10 | Tooltip,
11 | ResponsiveContainer,
12 | Legend,
13 | BarChart,
14 | Bar,
15 | LineChart,
16 | Line,
17 | } from 'recharts';
18 |
19 | const CartesianChart = ({ resultSet, children, ChartComponent }) => (
20 |
21 |
22 |
23 |
24 |
25 | {children}
26 |
27 |
28 |
29 |
30 | );
31 |
32 | const colors = ['#FF6492', '#141446', '#7A77FF'];
33 |
34 | const stackedChartData = (resultSet) => {
35 | const data = resultSet
36 | .pivot()
37 | .map(({ xValues, yValuesArray }) =>
38 | yValuesArray.map(([yValues, m]) => ({
39 | x: resultSet.axisValuesString(xValues, ', '),
40 | color: resultSet.axisValuesString(yValues, ', '),
41 | measure: m && Number.parseFloat(m),
42 | }))
43 | )
44 | .reduce((a, b) => a.concat(b), []);
45 |
46 | return data;
47 | };
48 |
49 | const TypeToChartComponent = {
50 | line: ({ resultSet }) => {
51 | return (
52 |
53 | {resultSet.seriesNames().map((series, i) => (
54 |
61 | ))}
62 |
63 | );
64 | },
65 | bar: ({ resultSet }) => {
66 | return (
67 |
68 | {resultSet.seriesNames().map((series, i) => (
69 |
76 | ))}
77 |
78 | );
79 | },
80 | area: ({ resultSet }) => {
81 | return (
82 |
83 | {resultSet.seriesNames().map((series, i) => (
84 |
92 | ))}
93 |
94 | );
95 | },
96 | pie: ({ resultSet }) => {
97 | return (
98 |
99 |
100 |
107 | {resultSet.chartPivot().map((e, index) => (
108 | |
109 | ))}
110 |
111 |
112 |
113 |
114 |
115 | );
116 | },
117 | };
118 |
--------------------------------------------------------------------------------
/packages/static-chart/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage, ChartSnippet } = require('@cubejs-templates/core');
2 |
3 | class StaticChartTemplate extends TemplatePackage {}
4 |
5 | module.exports = (context) => {
6 | const { chartCode } = context.playgroundContext;
7 |
8 | if (!chartCode) {
9 | throw new Error(`playgroundContext misses required chartCode`);
10 | }
11 |
12 | return new StaticChartTemplate(context, {
13 | '/src/pages/DashboardPage.js': new ChartSnippet(chartCode),
14 | });
15 | };
16 |
--------------------------------------------------------------------------------
/packages/templates-core/.gitignore:
--------------------------------------------------------------------------------
1 | # IDE / Vscode
2 | .vscode
3 |
4 | # IDE / Editor
5 | .idea
6 |
7 | # Dependency directories
8 | node_modules/
9 |
10 | # Logs
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 |
15 | # Mac OSX
16 | .DS_Store
17 |
18 | # Vim swap files
19 | *.swp
20 |
21 | playground
--------------------------------------------------------------------------------
/packages/templates-core/index.js:
--------------------------------------------------------------------------------
1 | const TemplatePackage = require('./src/TemplatePackage');
2 | const AppSnippet = require('./src/AppSnippet');
3 | const ChartSnippet = require('./src/ChartSnippet');
4 | const ChartRendererSnippet = require('./src/ChartRendererSnippet');
5 | const QueryRendererSnippet = require('./src/QueryRendererSnippet');
6 | const CredentialsSnippet = require('./src/CredentialsSnippet');
7 | const SourceSnippet = require('./src/SourceSnippet');
8 | const VueSourceSnippet = require('./src/VueSourceSnippet');
9 | const IndexSnippet = require('./src/IndexSnippet');
10 | const VueMainSnippet = require('./src/VueMainSnippet');
11 | const TargetSource = require('./src/TargetSource');
12 | const utils = require('./src/utils');
13 |
14 | module.exports = {
15 | TemplatePackage,
16 | IndexSnippet,
17 | AppSnippet,
18 | CredentialsSnippet,
19 | ChartSnippet,
20 | SourceSnippet,
21 | ChartRendererSnippet,
22 | QueryRendererSnippet,
23 | TargetSource,
24 | VueSourceSnippet,
25 | VueMainSnippet,
26 | utils,
27 | };
28 |
--------------------------------------------------------------------------------
/packages/templates-core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@cubejs-templates/core",
3 | "version": "0.0.8",
4 | "author": "Cube Dev, Inc.",
5 | "license": "Apache-2.0",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/cube-js/cubejs-playground-templates",
9 | "directory": "packages/templates-core"
10 | },
11 | "engines": {
12 | "node": ">=8.11.1"
13 | },
14 | "dependencies": {
15 | "@babel/generator": "^7.12.15",
16 | "@babel/parser": "^7.12.16",
17 | "@babel/preset-typescript": "^7.12.16",
18 | "@babel/traverse": "^7.12.9",
19 | "@babel/types": "^7.12.13",
20 | "fs-extra": "^9.0.1",
21 | "prettier": "^2.3.2",
22 | "semver": "^7.3.2"
23 | },
24 | "devDependencies": {
25 | "@types/babel-generator": "^6.25.3",
26 | "@types/babel-traverse": "^6.25.5",
27 | "@types/ramda": "^0.27.39"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/templates-core/src/AppSnippet.js:
--------------------------------------------------------------------------------
1 | const traverse = require('@babel/traverse').default;
2 | const t = require('@babel/types');
3 |
4 | const SourceSnippet = require('./SourceSnippet');
5 |
6 | class AppSnippet extends SourceSnippet {
7 | insertAnchor(targetSource) {
8 | let appClass = null;
9 | traverse(targetSource.ast, {
10 | FunctionDeclaration: (path) => {
11 | if (path.get('id').node.name === 'App') {
12 | appClass = path;
13 | }
14 | },
15 | });
16 | if (!appClass) {
17 | return super.insertAnchor(targetSource);
18 | }
19 | return appClass;
20 | }
21 |
22 | handleExistingMerge(existingDefinition, newDefinition) {
23 | if (
24 | existingDefinition &&
25 | existingDefinition.node.type === 'FunctionDeclaration'
26 | ) {
27 | existingDefinition.replaceWith(
28 | t.variableDeclaration('const', [newDefinition.node])
29 | );
30 | }
31 | }
32 | }
33 |
34 | module.exports = AppSnippet;
35 |
--------------------------------------------------------------------------------
/packages/templates-core/src/ChartRendererSnippet.js:
--------------------------------------------------------------------------------
1 | const traverse = require('@babel/traverse').default;
2 | const SourceSnippet = require('./SourceSnippet');
3 |
4 | class ChartRendererSnippet extends SourceSnippet {
5 | handleExistingMerge(existingDefinition, newDefinition) {
6 | if (existingDefinition.get('id').node.name === 'TypeToChartComponent') {
7 | const existingPropertyNames = existingDefinition
8 | .get('init')
9 | .get('properties')
10 | .map((p) => p.node.key.name);
11 | existingDefinition.get('init').pushContainer(
12 | 'properties',
13 | newDefinition.node.init.properties.filter(
14 | (p) => existingPropertyNames.indexOf(p.key.name) === -1
15 | )
16 | );
17 | } else {
18 | super.handleExistingMerge(existingDefinition, newDefinition);
19 | }
20 | }
21 |
22 | insertAnchor(targetSource) {
23 | let anchor = null;
24 | traverse(targetSource.ast, {
25 | VariableDeclaration: (path) => {
26 | if (
27 | path.get('declarations')[0].get('id').node.name ===
28 | 'TypeToChartComponent'
29 | ) {
30 | anchor = path;
31 | }
32 | },
33 | });
34 | if (!anchor) {
35 | throw new Error(
36 | `renderChart class not found. Can't parse dashboard app. Please delete dashboard-app directory and try to create it again.`
37 | );
38 | }
39 | return anchor;
40 | }
41 | }
42 |
43 | module.exports = ChartRendererSnippet;
44 |
--------------------------------------------------------------------------------
/packages/templates-core/src/ChartSnippet.js:
--------------------------------------------------------------------------------
1 | const traverse = require('@babel/traverse').default;
2 | const t = require('@babel/types');
3 | const SourceSnippet = require('./SourceSnippet');
4 |
5 | class ChartSnippet extends SourceSnippet {
6 | mergeTo(targetSource) {
7 | const dashboardItemsArray = targetSource.definitions.find(
8 | (d) =>
9 | d.get('id').node.type === 'Identifier' &&
10 | d.get('id').node.name === 'DashboardItems'
11 | );
12 |
13 | if (!dashboardItemsArray) {
14 | throw new Error(
15 | `DashboardItems array not found. Please use adding chart feature only with static dashboard`
16 | );
17 | }
18 |
19 | traverse(this.ast, {
20 | JSXOpeningElement: (path) => {
21 | if (path.get('name').get('name').node === 'QueryRenderer') {
22 | const query = path
23 | .get('attributes')
24 | .find((p) => p.get('name').get('name').node === 'query')
25 | .get('value')
26 | .get('expression');
27 |
28 | const rendererCall = path
29 | .get('attributes')
30 | .find((p) => p.get('name').get('name').node === 'render')
31 | .get('value')
32 | .get('expression');
33 |
34 | let chartType = 'line';
35 |
36 | try {
37 | const chartTypeNode = rendererCall.node.body.arguments[0].properties.find(
38 | (p) => {
39 | if (p.type === 'ObjectProperty' && p.key.name === 'chartType') {
40 | return p.value.value;
41 | }
42 | }
43 | );
44 |
45 | chartType = chartTypeNode.value.value;
46 | } catch (error) {
47 | console.log("Can't detect chart type", error);
48 | }
49 |
50 | dashboardItemsArray
51 | .get('init')
52 | .pushContainer(
53 | 'elements',
54 | t.objectExpression([
55 | t.objectProperty(
56 | t.identifier('id'),
57 | t.numericLiteral(
58 | dashboardItemsArray.get('init').get('elements').length
59 | )
60 | ),
61 | t.objectProperty(
62 | t.identifier('name'),
63 | t.stringLiteral('New Chart')
64 | ),
65 | t.objectProperty(
66 | t.identifier('vizState'),
67 | t.objectExpression([
68 | t.objectProperty(t.identifier('query'), query.node),
69 | t.objectProperty(
70 | t.identifier('chartType'),
71 | t.stringLiteral(chartType)
72 | ),
73 | ])
74 | ),
75 | ])
76 | );
77 | }
78 | },
79 | });
80 | }
81 | }
82 |
83 | module.exports = ChartSnippet;
84 |
--------------------------------------------------------------------------------
/packages/templates-core/src/CredentialsSnippet.js:
--------------------------------------------------------------------------------
1 | const t = require('@babel/types');
2 | const SourceSnippet = require('./SourceSnippet');
3 |
4 | class CredentialsSnippet extends SourceSnippet {
5 | constructor({ apiUrl, cubejsToken }) {
6 | super();
7 |
8 | this.apiUrl = apiUrl;
9 | this.cubejsToken = cubejsToken;
10 | }
11 |
12 | mergeTo(targetSource) {
13 | super.mergeTo(targetSource);
14 | this.replaceTokens(targetSource);
15 | }
16 |
17 | replaceTokens(targetSource) {
18 | const apiUrl = targetSource.definitions.find(
19 | (d) => d.get('id').node.name === 'API_URL'
20 | );
21 | apiUrl.get('init').replaceWith(t.stringLiteral(this.apiUrl));
22 |
23 | const cubejsToken = targetSource.definitions.find(
24 | (d) => d.get('id').node.name === 'CUBEJS_TOKEN'
25 | );
26 | cubejsToken.get('init').replaceWith(t.stringLiteral(this.cubejsToken));
27 | }
28 | }
29 |
30 | module.exports = CredentialsSnippet;
31 |
--------------------------------------------------------------------------------
/packages/templates-core/src/CssSourceSnippet.js:
--------------------------------------------------------------------------------
1 | class CssSourceSnippet {
2 | constructor(source) {
3 | this.source = source;
4 | }
5 |
6 | mergeTo(targetSource) {
7 | if (!targetSource.source) {
8 | targetSource.source = this.source;
9 | }
10 | }
11 | }
12 |
13 | module.exports = CssSourceSnippet;
14 |
--------------------------------------------------------------------------------
/packages/templates-core/src/CssTargetSource.js:
--------------------------------------------------------------------------------
1 | class CssTargetSource {
2 | constructor(fileName, source) {
3 | this.source = source;
4 | this.fileName = fileName;
5 | }
6 |
7 | formattedCode() {
8 | return this.source;
9 | }
10 | }
11 |
12 | module.exports = CssTargetSource;
13 |
--------------------------------------------------------------------------------
/packages/templates-core/src/HtmlSourceSnippet.js:
--------------------------------------------------------------------------------
1 | class HtmlSourceSnippet {
2 | constructor(source) {
3 | this.source = source;
4 | }
5 |
6 | mergeTo(targetSource) {
7 | if (!targetSource.source) {
8 | targetSource.source = this.source;
9 | }
10 | }
11 | }
12 |
13 | module.exports = HtmlSourceSnippet;
14 |
--------------------------------------------------------------------------------
/packages/templates-core/src/HtmlTargetSource.js:
--------------------------------------------------------------------------------
1 | class HtmlTargetSource {
2 | constructor(fileName, source) {
3 | this.source = source;
4 | this.fileName = fileName;
5 | }
6 |
7 | formattedCode() {
8 | return this.source;
9 | }
10 | }
11 |
12 | module.exports = HtmlTargetSource;
13 |
--------------------------------------------------------------------------------
/packages/templates-core/src/IndexSnippet.js:
--------------------------------------------------------------------------------
1 | const traverse = require('@babel/traverse').default;
2 | const SourceSnippet = require('./SourceSnippet');
3 |
4 | class IndexSnippet extends SourceSnippet {
5 | mergeTo(targetSource) {
6 | super.mergeTo(targetSource);
7 | this.replaceRouter(targetSource);
8 | }
9 |
10 | replaceRouter(targetSource) {
11 | let routerElement = null;
12 | traverse(targetSource.ast, {
13 | JSXOpeningElement: (path) => {
14 | if (path.get('name').get('name').node === 'Router') {
15 | routerElement = path;
16 | }
17 | },
18 | });
19 |
20 | if (!routerElement) {
21 | traverse(this.ast, {
22 | JSXOpeningElement: (path) => {
23 | if (path.get('name').get('name').node === 'Router') {
24 | routerElement = path;
25 | }
26 | },
27 | });
28 |
29 | if (!routerElement) {
30 | throw new Error(`Router element is not found`);
31 | }
32 |
33 | const targetPath = this.findAppOrRouter(targetSource);
34 | targetPath.replaceWith(routerElement.parentPath);
35 | }
36 | }
37 |
38 | findAppOrRouter(targetSource) {
39 | let appElement = null;
40 | traverse(targetSource.ast, {
41 | JSXOpeningElement: (path) => {
42 | if (
43 | path.get('name').get('name').node === 'Router' ||
44 | path.get('name').get('name').node === 'App'
45 | ) {
46 | appElement = path;
47 | }
48 | },
49 | });
50 | if (!appElement) {
51 | throw new Error(
52 | `App class not found. Can't parse dashboard app. Please delete the dashboard-app directory and try to create it again.`
53 | );
54 | }
55 | return appElement.parentPath;
56 | }
57 |
58 | insertAnchor(targetSource) {
59 | return this.findAppOrRouter(targetSource).parentPath.parentPath;
60 | }
61 | }
62 |
63 | module.exports = IndexSnippet;
64 |
--------------------------------------------------------------------------------
/packages/templates-core/src/QueryRendererSnippet.js:
--------------------------------------------------------------------------------
1 | const traverse = require('@babel/traverse').default;
2 | const SourceSnippet = require('./SourceSnippet');
3 |
4 | class QueryRendererSnippet extends SourceSnippet {
5 | mergeTo(targetSource) {
6 | super.mergeTo(targetSource);
7 |
8 | const methods = [];
9 | const properties = [];
10 |
11 | traverse(this.ast, {
12 | ClassDeclaration(path) {
13 | // if (path.get('id').node.name === 'QueryRendererComponent') {
14 | traverse(
15 | path.node,
16 | {
17 | ClassMethod(path) {
18 | methods.push(path);
19 | },
20 | ClassProperty(path) {
21 | properties.push(path);
22 | },
23 | },
24 | path.scope,
25 | path.state,
26 | path.parentPath
27 | );
28 | // }
29 | },
30 | });
31 |
32 | traverse(targetSource.ast, {
33 | ClassDeclaration(path) {
34 | // if (path.get('id').node.name === 'QueryRendererComponent') {
35 | properties.concat(methods).forEach(({ node }) => {
36 | path.get('body').unshiftContainer('body', node);
37 | });
38 | // }
39 | },
40 | });
41 | }
42 | }
43 |
44 | module.exports = QueryRendererSnippet;
45 |
--------------------------------------------------------------------------------
/packages/templates-core/src/TargetSource.js:
--------------------------------------------------------------------------------
1 | const traverse = require('@babel/traverse').default;
2 |
3 | const SourceSnippet = require('./SourceSnippet');
4 | const VueSourceSnippet = require('./VueSourceSnippet');
5 |
6 | class TargetSource {
7 | constructor(fileName, source) {
8 | this.snippet = null;
9 | this.imports = [];
10 | this.definitions = [];
11 |
12 | this.source = source;
13 | this.fileName = fileName;
14 |
15 | this.parseSourceCode();
16 |
17 | if (this.snippet && this.ast) {
18 | this.findAllImports();
19 | this.findAllDefinitions();
20 | this.findDefaultExport();
21 | }
22 | }
23 |
24 | get ast() {
25 | return this.snippet.ast;
26 | }
27 |
28 | parseSourceCode() {
29 | if (this.fileName.endsWith('.vue')) {
30 | this.snippet = new VueSourceSnippet(this.source);
31 | } else if (this.fileName.match(/\.([tj]sx?)$/)) {
32 | this.snippet = new SourceSnippet(this.source);
33 | } else {
34 | console.log(`Skip parsing '${this.fileName}'. No parser found.`);
35 | }
36 | }
37 |
38 | findAllImports() {
39 | this.imports = [];
40 |
41 | traverse(this.ast, {
42 | ImportDeclaration: (path) => {
43 | this.imports.push(path);
44 | },
45 | });
46 | }
47 |
48 | findDefaultExport() {
49 | traverse(this.ast, {
50 | ExportDefaultDeclaration: (path) => {
51 | if (path) {
52 | this.defaultExport = path;
53 | }
54 | },
55 | });
56 | }
57 |
58 | findAllDefinitions() {
59 | this.definitions = [];
60 |
61 | traverse(this.ast, {
62 | VariableDeclaration: (path) => {
63 | if (path.parent.type === 'Program') {
64 | this.definitions.push(...path.get('declarations'));
65 | }
66 | },
67 | FunctionDeclaration: (path) => {
68 | if (path.parent.type === 'Program') {
69 | this.definitions.push(path);
70 | }
71 | },
72 | });
73 | }
74 |
75 | code() {
76 | return this.snippet.source;
77 | }
78 |
79 | formattedCode() {
80 | return SourceSnippet.formatCode(
81 | this.code(),
82 | this.fileName.match(/\.(tsx?)$/) ? 'typescript' : 'babel'
83 | );
84 | }
85 |
86 | getImportDependencies() {
87 | return this.imports
88 | .filter((i) => {
89 | const { value } = i.get('source').node;
90 | if (value.startsWith('.') || value.startsWith('@/')) {
91 | return false;
92 | }
93 |
94 | return true;
95 | })
96 | .map((i) => {
97 | const importName = i.get('source').node.value.split('/');
98 | const dependency =
99 | importName[0].indexOf('@') === 0
100 | ? [importName[0], importName[1]].join('/')
101 | : importName[0];
102 | return dependency;
103 | });
104 | }
105 | }
106 |
107 | module.exports = TargetSource;
108 |
--------------------------------------------------------------------------------
/packages/templates-core/src/VueMainSnippet.js:
--------------------------------------------------------------------------------
1 | const traverse = require('@babel/traverse').default;
2 | const t = require('@babel/types');
3 |
4 | const SourceSnippet = require('./SourceSnippet');
5 |
6 | class VueMainSnippet extends SourceSnippet {
7 | mergeTo(targetSource) {
8 | super.mergeTo(targetSource);
9 | this.extractPlugins(targetSource);
10 | }
11 |
12 | extractPlugins(targetSource) {
13 | const vuePlugins = [];
14 | traverse(this.ast, {
15 | CallExpression: (path) => {
16 | if (
17 | t.isMemberExpression(path.node.callee) &&
18 | t.isIdentifier(path.node.callee.object) &&
19 | t.isIdentifier(path.node.callee.property)
20 | ) {
21 | if (
22 | path.node.callee.object.name === 'Vue' &&
23 | path.node.callee.property.name === 'use'
24 | ) {
25 | vuePlugins.push(path.node);
26 | }
27 | }
28 | },
29 | });
30 |
31 | traverse(targetSource.ast, {
32 | CallExpression: (path) => {
33 | if (
34 | t.isMemberExpression(path.node.callee) &&
35 | t.isNewExpression(path.node.callee.object) &&
36 | t.isIdentifier(path.node.callee.object.callee) &&
37 | path.node.callee.object.callee.name === 'Vue'
38 | ) {
39 | path.insertBefore(vuePlugins);
40 | }
41 | },
42 | });
43 |
44 | let targetPropertiesRef = null;
45 | const optionsArgSet = new Set();
46 |
47 | this.traverseVueOptions(targetSource.ast, (node) => {
48 | const [optionsArg] = node.arguments;
49 | if (optionsArg && t.isObjectExpression(optionsArg)) {
50 | targetPropertiesRef = optionsArg.properties;
51 | optionsArg.properties.forEach((node) => {
52 | optionsArgSet.add(SourceSnippet.astToCode(node));
53 | });
54 | }
55 | });
56 |
57 | this.traverseVueOptions(this.ast, (node) => {
58 | const [optionsArg] = node.arguments;
59 | if (optionsArg && t.isObjectExpression(optionsArg)) {
60 | optionsArg.properties.forEach((prop) => {
61 | if (!optionsArgSet.has(SourceSnippet.astToCode(prop))) {
62 | targetPropertiesRef.push(prop);
63 | }
64 | });
65 | }
66 | });
67 | }
68 |
69 | traverseVueOptions(ast, cb) {
70 | traverse(ast, {
71 | CallExpression: (path) => {
72 | if (
73 | t.isMemberExpression(path.node.callee) &&
74 | t.isNewExpression(path.node.callee.object) &&
75 | t.isIdentifier(path.node.callee.object.callee) &&
76 | path.node.callee.object.callee.name === 'Vue'
77 | ) {
78 | cb(path.node.callee.object);
79 | }
80 | },
81 | });
82 | }
83 | }
84 |
85 | module.exports = VueMainSnippet;
86 |
--------------------------------------------------------------------------------
/packages/templates-core/src/utils.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs-extra');
2 | const path = require('path');
3 |
4 | async function fileContentsRecursive(dir, rootPath, includeNodeModules) {
5 | if (!rootPath) {
6 | rootPath = dir;
7 | }
8 | if (!(await fs.pathExists(dir))) {
9 | return [];
10 | }
11 | if (dir.indexOf('node_modules') !== -1 && !includeNodeModules) {
12 | return [];
13 | }
14 |
15 | const files = fs.readdirSync(dir).filter((name) => !name.includes('.json'));
16 |
17 | // const files = await fs.readdir(dir);
18 | return (
19 | await Promise.all(
20 | files.map(async (file) => {
21 | const fileName = path.join(dir, file);
22 | const stats = await fs.lstat(fileName);
23 | if (!stats.isDirectory()) {
24 | const content = await fs.readFile(fileName, 'utf-8');
25 | return [
26 | {
27 | fileName: fileName.replace(rootPath, '').replace(/\\/g, '/'),
28 | content,
29 | },
30 | ];
31 | } else {
32 | return fileContentsRecursive(fileName, rootPath, includeNodeModules);
33 | }
34 | })
35 | )
36 | ).reduce((a, b) => a.concat(b), []);
37 | }
38 |
39 | module.exports = {
40 | fileContentsRecursive,
41 | };
42 |
--------------------------------------------------------------------------------
/packages/templates-core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "esnext",
4 | "target": "ES5",
5 | "declaration": true,
6 | "noEmitOnError": true,
7 | "sourceMap": true,
8 | "allowJs": true,
9 | "checkJs": true,
10 | "jsx": "react-jsx",
11 | "allowSyntheticDefaultImports": true,
12 | "lib": ["dom", "dom.iterable", "esnext"],
13 | "skipLibCheck": true,
14 | "esModuleInterop": true,
15 | "strict": true,
16 | "forceConsistentCasingInFileNames": true,
17 | "noFallthroughCasesInSwitch": true,
18 | "moduleResolution": "node",
19 | "resolveJsonModule": true,
20 | "isolatedModules": true,
21 | "noImplicitAny": false,
22 | "noEmit": true
23 | },
24 | "exclude": ["node_modules"],
25 | "include": ["src", "tests"]
26 | }
27 |
--------------------------------------------------------------------------------
/packages/vue-charting-library/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage } = require('@cubejs-templates/core');
2 |
3 | class VueChartingLibraryTemplate extends TemplatePackage {}
4 |
5 | module.exports = (context) => new VueChartingLibraryTemplate(context);
6 |
--------------------------------------------------------------------------------
/packages/vue-charting-library/scaffolding/src/components/ChartRenderer.vue:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/vue-chartjs-charts/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage, VueMainSnippet } = require('@cubejs-templates/core');
2 |
3 | class VueChartjsChartsTemplate extends TemplatePackage {}
4 |
5 | module.exports = (context) =>
6 | new VueChartjsChartsTemplate(context, {
7 | '/src/main.js': new VueMainSnippet(),
8 | });
9 |
--------------------------------------------------------------------------------
/packages/vue-chartjs-charts/scaffolding/src/components/ChartRenderer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
67 |
68 |
75 |
--------------------------------------------------------------------------------
/packages/vue-chartjs-charts/scaffolding/src/components/LineChart.vue:
--------------------------------------------------------------------------------
1 |
34 |
--------------------------------------------------------------------------------
/packages/vue-chartjs-charts/scaffolding/src/components/Table.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
38 |
--------------------------------------------------------------------------------
/packages/vue-chartkick-charts/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage, VueMainSnippet } = require('@cubejs-templates/core');
2 |
3 | class VueChartkickChartsTemplate extends TemplatePackage {
4 | importDependencies() {
5 | return {
6 | 'vue-chartkick': '^0.6.0',
7 | };
8 | }
9 | }
10 |
11 | module.exports = (context) =>
12 | new VueChartkickChartsTemplate(context, {
13 | '/src/main.js': new VueMainSnippet(),
14 | });
15 |
--------------------------------------------------------------------------------
/packages/vue-chartkick-charts/scaffolding/src/components/ChartRenderer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 |
24 |
29 | {{ item.series[0].value }}
30 |
31 |
32 |
33 |
34 |
109 |
--------------------------------------------------------------------------------
/packages/vue-chartkick-charts/scaffolding/src/components/Table.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
39 |
44 |
--------------------------------------------------------------------------------
/packages/vue-chartkick-charts/scaffolding/src/main.js:
--------------------------------------------------------------------------------
1 | import Chart from 'chart.js';
2 | import VueChartkick from 'vue-chartkick';
3 |
4 | Vue.use(VueChartkick, { adapter: Chart });
5 |
--------------------------------------------------------------------------------
/packages/vue-charts/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage, VueMainSnippet } = require('@cubejs-templates/core');
2 |
3 | class VueChartsTemplate extends TemplatePackage {
4 | importDependencies() {
5 | return {
6 | 'vue-cli-plugin-vuetify': '~2.0.6',
7 | 'vuetify-loader': '^1.3.0',
8 | };
9 | }
10 | }
11 |
12 | module.exports = (context) =>
13 | new VueChartsTemplate(context, {
14 | '/src/main.js': new VueMainSnippet(),
15 | });
16 |
--------------------------------------------------------------------------------
/packages/vue-charts/scaffolding/.eslintignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/packages/vue-charts/scaffolding/src/ChartContainer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
128 |
--------------------------------------------------------------------------------
/packages/vue-charts/scaffolding/src/ChartRenderer.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
31 |
--------------------------------------------------------------------------------
/packages/vue-charts/scaffolding/src/main.js:
--------------------------------------------------------------------------------
1 | import vuetify from './plugins/vuetify';
2 |
3 | new Vue({
4 | vuetify,
5 | }).$mount('#app');
6 |
--------------------------------------------------------------------------------
/packages/vue-charts/scaffolding/src/plugins/vuetify.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuetify from 'vuetify/lib';
3 |
4 | Vue.use(Vuetify);
5 |
6 | export default new Vuetify({
7 | theme: {
8 | themes: {
9 | light: {
10 | primary: '#7A77FF',
11 | },
12 | },
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/packages/vue-charts/scaffolding/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transpileDependencies: ['vuetify'],
3 | css: {
4 | extract: false,
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/packages/vue-ui-test/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage } = require('@cubejs-templates/core');
2 |
3 | class VueVuetifyDynamicTemplate extends TemplatePackage {
4 | importDependencies() {
5 | return {
6 | sass: '^1.19.0',
7 | 'sass-loader': '^8.0.0',
8 | 'vue-cli-plugin-vuetify': '~2.0.6',
9 | 'vuetify-loader': '^1.3.0',
10 | };
11 | }
12 | }
13 |
14 | module.exports = (context) => new VueVuetifyDynamicTemplate(context);
15 |
--------------------------------------------------------------------------------
/packages/vue-ui-test/scaffolding/src/assets/github.svg:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueRouter from 'vue-router';
3 | import VueApollo from 'vue-apollo';
4 | import cubejs from '@cubejs-client/core';
5 |
6 | import App from './App.vue';
7 | import vuetify from './plugins/vuetify';
8 | import Explore from './pages/explore/Explore.vue';
9 | import Dashboard from './pages/dashboard/Dashboard.vue';
10 | import apolloClient from './graphql/client';
11 |
12 | Vue.use(VueApollo);
13 |
14 | const apolloProvider = new VueApollo({
15 | defaultClient: apolloClient,
16 | });
17 |
18 | const API_URL = 'https://ecom.cubecloudapp.dev';
19 | const CUBEJS_TOKEN =
20 | 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1OTQ2NjY4OTR9.0fdi5cuDZ2t3OSrPOMoc3B1_pwhnWj4ZmM3FHEX7Aus';
21 |
22 | const cubejsApi = cubejs(CUBEJS_TOKEN, {
23 | apiUrl: `${API_URL}/cubejs-api/v1`,
24 | });
25 |
26 | const router = new VueRouter({
27 | routes: [
28 | { path: '/', component: Explore, props: { cubejsApi } },
29 | { path: '/explore', component: Explore, props: { cubejsApi } },
30 | { path: '/dashboard', component: Dashboard, props: { cubejsApi } },
31 | ],
32 | });
33 |
34 | Vue.config.productionTip = false;
35 |
36 | Vue.use(VueRouter);
37 |
38 | new Vue({
39 | router,
40 | vuetify,
41 | apolloProvider,
42 | render: (h) => h(App),
43 | }).$mount('#app');
44 |
--------------------------------------------------------------------------------
/packages/vue-ui-test/scaffolding/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueRouter from 'vue-router';
3 | import VueApollo from 'vue-apollo';
4 | import cubejs from '@cubejs-client/core';
5 |
6 | import App from './App.vue';
7 | import vuetify from './plugins/vuetify';
8 | import Explore from './pages/explore/Explore.vue';
9 | import Dashboard from './pages/dashboard/Dashboard.vue';
10 | import apolloClient from './graphql/client';
11 |
12 | Vue.use(VueApollo);
13 |
14 | const apolloProvider = new VueApollo({
15 | defaultClient: apolloClient,
16 | });
17 |
18 | const API_URL = 'https://ecom.cubecloudapp.dev';
19 | const CUBEJS_TOKEN =
20 | 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1OTQ2NjY4OTR9.0fdi5cuDZ2t3OSrPOMoc3B1_pwhnWj4ZmM3FHEX7Aus';
21 |
22 | const cubejsApi = cubejs(CUBEJS_TOKEN, {
23 | apiUrl: `${API_URL}/cubejs-api/v1`,
24 | });
25 |
26 | const router = new VueRouter({
27 | routes: [
28 | { path: '/', component: Explore, props: { cubejsApi } },
29 | { path: '/explore', component: Explore, props: { cubejsApi } },
30 | { path: '/dashboard', component: Dashboard, props: { cubejsApi } },
31 | ],
32 | });
33 |
34 | Vue.config.productionTip = false;
35 |
36 | Vue.use(VueRouter);
37 |
38 | new Vue({
39 | router,
40 | vuetify,
41 | apolloProvider,
42 | render: (h) => h(App),
43 | }).$mount('#app');
44 |
--------------------------------------------------------------------------------
/packages/vue-ui-test/scaffolding/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transpileDependencies: ['vuetify'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage } = require('@cubejs-templates/core');
2 |
3 | class VueVuetifyDynamicTemplate extends TemplatePackage {
4 | importDependencies() {
5 | return {
6 | sass: '^1.19.0',
7 | 'sass-loader': '^8.0.0',
8 | 'vue-cli-plugin-vuetify': '~2.0.6',
9 | 'vuetify-loader': '^1.3.0',
10 | 'graphql-tools': '5.0.0',
11 | 'chart.js': '^2.9.4',
12 | };
13 | }
14 | }
15 |
16 | module.exports = (context) => new VueVuetifyDynamicTemplate(context);
17 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/.eslintignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
15 | Explore
16 | Dashboard
17 |
18 |
19 |
20 |
21 |
22 |
27 |
36 |
45 |
46 |
47 |
48 |
49 |
50 |
59 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
94 |
95 |
115 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cube-js/cube-playground-templates/365141994ebb4b76f4e31686d04b7d5cfd545047/packages/vue-vuetify-dynamic/scaffolding/src/assets/logo.png
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/assets/slack.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/assets/slackWhite.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/graphql/mutations.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 | export const CREATE_DASHBOARD_ITEM = gql`
3 | mutation CreateDashboardItem($input: DashboardItemInput) {
4 | createDashboardItem(input: $input) {
5 | id
6 | layout
7 | vizState
8 | name
9 | type
10 | }
11 | }
12 | `;
13 | export const UPDATE_DASHBOARD_ITEM = gql`
14 | mutation UpdateDashboardItem($id: String!, $input: DashboardItemInput) {
15 | updateDashboardItem(id: $id, input: $input) {
16 | id
17 | layout
18 | vizState
19 | name
20 | type
21 | }
22 | }
23 | `;
24 | export const DELETE_DASHBOARD_ITEM = gql`
25 | mutation DeleteDashboardItem($id: String!) {
26 | deleteDashboardItem(id: $id) {
27 | id
28 | layout
29 | vizState
30 | name
31 | type
32 | }
33 | }
34 | `;
35 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/graphql/queries.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 | export const GET_DASHBOARD_ITEMS = gql`
3 | query GetDashboardItems {
4 | dashboardItems {
5 | id
6 | layout
7 | vizState
8 | name
9 | type
10 | }
11 | }
12 | `;
13 | export const GET_DASHBOARD_ITEM = gql`
14 | query GetDashboardItem($id: String!) {
15 | dashboardItem(id: $id) {
16 | id
17 | layout
18 | vizState
19 | name
20 | type
21 | }
22 | }
23 | `;
24 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueRouter from 'vue-router';
3 | import VueApollo from 'vue-apollo';
4 | import cubejs from '@cubejs-client/core';
5 |
6 | import App from './App.vue';
7 | import vuetify from './plugins/vuetify';
8 | import Explore from './pages/explore/Explore.vue';
9 | import Dashboard from './pages/dashboard/Dashboard.vue';
10 | import apolloClient from './graphql/client';
11 |
12 | Vue.use(VueApollo);
13 |
14 | const apolloProvider = new VueApollo({
15 | defaultClient: apolloClient,
16 | });
17 |
18 | const API_URL = 'https://ecom.cubecloudapp.dev';
19 | const CUBEJS_TOKEN =
20 | 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1OTQ2NjY4OTR9.0fdi5cuDZ2t3OSrPOMoc3B1_pwhnWj4ZmM3FHEX7Aus';
21 |
22 | const cubejsApi = cubejs(CUBEJS_TOKEN, {
23 | apiUrl: `${API_URL}/cubejs-api/v1`,
24 | });
25 |
26 | const router = new VueRouter({
27 | routes: [
28 | { path: '/', component: Explore, props: { cubejsApi } },
29 | { path: '/explore', component: Explore, props: { cubejsApi } },
30 | { path: '/dashboard', component: Dashboard, props: { cubejsApi } },
31 | ],
32 | });
33 |
34 | Vue.config.productionTip = false;
35 |
36 | Vue.use(VueRouter);
37 |
38 | new Vue({
39 | router,
40 | vuetify,
41 | apolloProvider,
42 | render: (h) => h(App),
43 | }).$mount('#app');
44 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/pages/dashboard/Dashboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
54 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/DateRangeSelect.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
46 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/MeasureSetter.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
20 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/TimeDimensionSelect.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/dialogs/AddToDashboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Add to Dashboard
5 |
6 |
7 |
8 |
9 | Add to dashboard
10 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
22 | Close
23 |
24 |
25 | Save
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
51 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/dialogs/Limit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Limit
5 |
6 |
7 |
8 | Limit
9 |
10 | $emit('update', Number(limit))"
16 | >
17 |
18 |
19 |
20 |
21 |
22 |
47 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/dialogs/Order.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Order
5 |
6 |
7 |
8 | Order
9 |
10 |
11 |
12 |
13 | mdi-arrow-all
14 |
15 | {{ member.title }}
16 |
17 |
18 |
$emit('orderChange', member.id, value)"
22 | >
23 | ASC
24 | DESC
25 | NONE
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
77 |
78 |
94 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/src/plugins/vuetify.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuetify from 'vuetify/lib';
3 |
4 | Vue.use(Vuetify);
5 |
6 | export default new Vuetify({
7 | theme: {
8 | themes: {
9 | light: {
10 | primary: '#7A77FF',
11 | },
12 | },
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-dynamic/scaffolding/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transpileDependencies: ['vuetify'],
3 | css: {
4 | extract: false,
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-tables/index.js:
--------------------------------------------------------------------------------
1 | const { TemplatePackage } = require('@cubejs-templates/core');
2 |
3 | class VueVuetifyTablesTemplate extends TemplatePackage {}
4 |
5 | // todo: VueTableRendererSnippet
6 |
7 | module.exports = (context) => new VueVuetifyTablesTemplate(context);
8 |
--------------------------------------------------------------------------------
/packages/vue-vuetify-tables/scaffolding/src/components/Table.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
37 |
--------------------------------------------------------------------------------