├── .gitignore ├── .prettierrc ├── README.md ├── manifest.json ├── package.json ├── packages ├── angular-charts │ ├── index.js │ └── scaffolding │ │ └── src │ │ ├── app │ │ ├── app.module.ts │ │ └── main │ │ │ ├── main.component.html │ │ │ └── main.component.ts │ │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ │ └── styles.css ├── angular-ng2-charts │ ├── index.js │ └── scaffolding │ │ └── app │ │ └── query-renderer │ │ ├── query-renderer.component.html │ │ ├── query-renderer.component.ts │ │ └── utils.ts ├── antd-tables │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── components │ │ └── ChartRenderer.js ├── bizcharts-charts │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── components │ │ └── ChartRenderer.js ├── chartjs-charts │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── components │ │ └── ChartRenderer.js ├── create-ng-app │ └── index.js ├── create-react-app │ └── index.js ├── create-vue-app │ └── index.js ├── d3-charts │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── components │ │ └── ChartRenderer.js ├── dev-cnga │ ├── index.js │ └── scaffolding │ │ ├── .angular.cli.json │ │ ├── package.json │ │ ├── src │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.ts │ │ │ └── app.module.ts │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ └── styles.css │ │ └── tsconfig.json ├── dev-cra │ ├── index.js │ └── scaffolding │ │ ├── package.json │ │ └── src │ │ ├── App.css │ │ ├── App.js │ │ └── index.js ├── dev-cva │ ├── index.js │ └── scaffolding │ │ ├── babel.config.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── public │ │ └── index.html │ │ └── src │ │ ├── App.vue │ │ └── main.js ├── material-tables │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── components │ │ └── ChartRenderer.js ├── ng-credentials │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── app │ │ └── app.module.ts ├── ng-material-dynamic │ ├── index.js │ └── scaffolding │ │ └── src │ │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── dashboard │ │ │ ├── dashboard.component.css │ │ │ ├── dashboard.component.html │ │ │ └── dashboard.component.ts │ │ ├── explore │ │ │ ├── add-to-dashboard-dialog │ │ │ │ ├── add-to-dashboard-dialog.component.css │ │ │ │ ├── add-to-dashboard-dialog.component.html │ │ │ │ └── add-to-dashboard-dialog.component.ts │ │ │ ├── explore.component.css │ │ │ ├── explore.component.html │ │ │ ├── explore.component.ts │ │ │ ├── filter-group │ │ │ │ ├── filter-group.component.css │ │ │ │ ├── filter-group.component.html │ │ │ │ ├── filter-group.component.ts │ │ │ │ └── filter.component.html │ │ │ ├── members-group │ │ │ │ ├── members-group.component.css │ │ │ │ ├── members-group.component.html │ │ │ │ └── members-group.component.ts │ │ │ ├── order │ │ │ │ ├── order.component.css │ │ │ │ ├── order.component.html │ │ │ │ └── order.component.ts │ │ │ ├── pivot │ │ │ │ ├── pivot.component.css │ │ │ │ ├── pivot.component.html │ │ │ │ └── pivot.component.ts │ │ │ ├── query-renderer │ │ │ │ └── query-renderer.component.ts │ │ │ └── time-group │ │ │ │ ├── time-group.component.html │ │ │ │ └── time-group.component.ts │ │ └── settings-dialog │ │ │ ├── settings-dialog.component.html │ │ │ └── settings-dialog.component.ts │ │ ├── graphql │ │ └── client.ts │ │ ├── index.html │ │ └── styles.css ├── ng2-charts │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── app │ │ └── explore │ │ └── query-renderer │ │ ├── query-renderer.component.css │ │ ├── query-renderer.component.html │ │ ├── query-renderer.component.ts │ │ └── utils.ts ├── react-antd-dynamic │ ├── index.js │ └── scaffolding │ │ └── src │ │ ├── App.js │ │ ├── body.css │ │ ├── components │ │ ├── ChartRenderer.js │ │ ├── Dashboard.js │ │ ├── DashboardItem.js │ │ ├── Header.js │ │ ├── QueryBuilder │ │ │ ├── ButtonDropdown.js │ │ │ ├── ExploreQueryBuilder.js │ │ │ ├── FilterGroup.js │ │ │ ├── FilterInput.js │ │ │ ├── MemberDropdown.js │ │ │ ├── MemberGroup.js │ │ │ ├── Order │ │ │ │ ├── DraggableItem.js │ │ │ │ └── OrderGroup.js │ │ │ ├── Pivot │ │ │ │ ├── Axes.js │ │ │ │ ├── DroppableArea.js │ │ │ │ ├── Item.js │ │ │ │ ├── Options.js │ │ │ │ └── Pivot.js │ │ │ ├── RemoveButtonGroup.js │ │ │ ├── SelectChartType.js │ │ │ └── TimeGroup.js │ │ └── TitleModal.js │ │ ├── graphql │ │ ├── client.js │ │ ├── mutations.js │ │ └── queries.js │ │ ├── index.js │ │ └── pages │ │ ├── DashboardPage.js │ │ └── ExplorePage.js ├── react-antd-static │ ├── index.js │ └── scaffolding │ │ └── src │ │ ├── App.js │ │ ├── body.css │ │ ├── components │ │ ├── ChartRenderer.js │ │ ├── Dashboard.js │ │ ├── DashboardItem.js │ │ └── Header.js │ │ ├── index.js │ │ └── pages │ │ └── DashboardPage.js ├── react-charting-library │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── components │ │ └── ChartRenderer.js ├── react-charts │ ├── index.js │ └── scaffolding │ │ └── src │ │ ├── App.js │ │ ├── ChartContainer.js │ │ ├── codegen.js │ │ └── style.css ├── react-credentials │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── App.js ├── react-material-static │ ├── index.js │ └── scaffolding │ │ └── src │ │ ├── App.js │ │ ├── body.css │ │ ├── components │ │ ├── ChartRenderer.js │ │ ├── Dashboard.js │ │ ├── DashboardItem.js │ │ └── Header.js │ │ ├── index.js │ │ └── pages │ │ └── DashboardPage.js ├── react-web-socket-transport │ ├── index.js │ └── scaffolding │ │ └── src │ │ ├── App.0.js │ │ └── App.js ├── recharts-charts │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── components │ │ └── ChartRenderer.js ├── static-chart │ └── index.js ├── templates-core │ ├── .gitignore │ ├── index.js │ ├── package.json │ ├── src │ │ ├── AppSnippet.js │ │ ├── ChartRendererSnippet.js │ │ ├── ChartSnippet.js │ │ ├── CredentialsSnippet.js │ │ ├── CssSourceSnippet.js │ │ ├── CssTargetSource.js │ │ ├── HtmlSourceSnippet.js │ │ ├── HtmlTargetSource.js │ │ ├── IndexSnippet.js │ │ ├── QueryRendererSnippet.js │ │ ├── SourceSnippet.js │ │ ├── TargetSource.js │ │ ├── TemplatePackage.js │ │ ├── VueMainSnippet.js │ │ ├── VueSourceSnippet.js │ │ └── utils.js │ ├── tsconfig.json │ └── yarn.lock ├── vue-charting-library │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── components │ │ └── ChartRenderer.vue ├── vue-chartjs-charts │ ├── index.js │ └── scaffolding │ │ └── src │ │ └── components │ │ ├── ChartRenderer.vue │ │ ├── LineChart.vue │ │ └── Table.vue ├── vue-chartkick-charts │ ├── index.js │ └── scaffolding │ │ └── src │ │ ├── components │ │ ├── ChartRenderer.vue │ │ └── Table.vue │ │ └── main.js ├── vue-charts │ ├── index.js │ └── scaffolding │ │ ├── .eslintignore │ │ ├── src │ │ ├── ChartContainer.vue │ │ ├── ChartRenderer.vue │ │ ├── main.js │ │ └── plugins │ │ │ └── vuetify.js │ │ └── vue.config.js ├── vue-ui-test │ ├── index.js │ └── scaffolding │ │ ├── src │ │ ├── assets │ │ │ └── github.svg │ │ ├── main.js │ │ └── pages │ │ │ └── Grid.vue │ │ └── vue.config.js ├── vue-vuetify-dynamic │ ├── index.js │ └── scaffolding │ │ ├── .eslintignore │ │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ ├── github.svg │ │ │ ├── githubWhite.svg │ │ │ ├── logo.png │ │ │ ├── slack.svg │ │ │ └── slackWhite.svg │ │ ├── graphql │ │ │ ├── client.js │ │ │ ├── mutations.js │ │ │ └── queries.js │ │ ├── main.js │ │ ├── pages │ │ │ ├── dashboard │ │ │ │ ├── Dashboard.vue │ │ │ │ └── components │ │ │ │ │ └── Grid.vue │ │ │ └── explore │ │ │ │ ├── Explore.vue │ │ │ │ └── components │ │ │ │ ├── DateRangeSelect.vue │ │ │ │ ├── FilterComponent.vue │ │ │ │ ├── MeasureSetter.vue │ │ │ │ ├── TimeDimensionSelect.vue │ │ │ │ └── dialogs │ │ │ │ ├── AddToDashboard.vue │ │ │ │ ├── Limit.vue │ │ │ │ ├── Order.vue │ │ │ │ └── PivotConfig.vue │ │ └── plugins │ │ │ └── vuetify.js │ │ └── vue.config.js └── vue-vuetify-tables │ ├── index.js │ └── scaffolding │ └── src │ └── components │ └── Table.vue └── yarn.lock /.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 | dist/ 21 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "arrowParens": "always", 8 | "trailingComma": "es5", 9 | "bracketSpacing": true, 10 | "jsxBracketSameLine": false, 11 | "overrides": [ 12 | { 13 | "files": ["*.css", "*.scss"], 14 | "options": { 15 | "singleQuote": false 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Cube.js Playground Templates", 3 | "author": "Cube Dev, Inc.", 4 | "license": "MIT", 5 | "devDependencies": { 6 | "husky": "^4.2.5", 7 | "prettier": "^2.0.5", 8 | "pretty-quick": "^2.0.1" 9 | }, 10 | "husky": { 11 | "hooks": { 12 | "pre-commit": "pretty-quick --staged" 13 | } 14 | }, 15 | "dependencies": { 16 | "@cubejs-client/core": "^0.23.11", 17 | "@cubejs-client/react": "^0.23.11", 18 | "@cubejs-templates/core": "latest", 19 | "bizcharts": "^4.0.14", 20 | "fs-extra": "^10.0.0", 21 | "is-docker": "^2.1.1", 22 | "recharts": "^1.8.5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/angular-charts/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | TemplatePackage, 3 | // CredentialsSnippet, 4 | } = require('@cubejs-templates/core'); 5 | 6 | class AngularChartsTemplate extends TemplatePackage { 7 | importDependencies() { 8 | return { 9 | '@angular/animations': '~10.2.4', 10 | '@angular/material': '~10.2.4', 11 | '@angular/cdk': '~10.2.4', 12 | 'ngx-spinner': '^10.0.1', 13 | 'chart.js': '^2.9.4', 14 | }; 15 | } 16 | } 17 | 18 | module.exports = (context) => { 19 | if (!context.playgroundContext) { 20 | throw new Error('"playgroundContext" is required'); 21 | } 22 | 23 | return new AngularChartsTemplate(context); 24 | }; 25 | -------------------------------------------------------------------------------- /packages/angular-charts/scaffolding/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 3 | import { NgModule } from '@angular/core'; 4 | import { CubejsClientModule } from '@cubejs-client/ngx'; 5 | import { ChartsModule } from 'ng2-charts'; 6 | import { MatTableModule } from '@angular/material/table'; 7 | import { NgxSpinnerModule } from 'ngx-spinner'; 8 | 9 | import { MainComponent } from './main/main.component'; 10 | import { AppComponent } from './app.component'; 11 | import { AngularNg2Charts } from './angular-ng2-charts/query-renderer.component'; 12 | import { AngularTestCharts } from './angular-test-charts/query-renderer.component'; 13 | 14 | const API_URL = undefined; 15 | const CUBEJS_TOKEN = undefined; 16 | 17 | const { apiUrl, token } = window.parent.window['__cubejsPlayground'] || {}; 18 | 19 | const cubejsOptions = { 20 | token: token || CUBEJS_TOKEN, 21 | options: { 22 | apiUrl: apiUrl || API_URL, 23 | }, 24 | }; 25 | 26 | @NgModule({ 27 | declarations: [ 28 | MainComponent, 29 | AppComponent, 30 | AngularNg2Charts, 31 | AngularTestCharts, 32 | ], 33 | imports: [ 34 | BrowserModule, 35 | CubejsClientModule.forRoot(cubejsOptions), 36 | ChartsModule, 37 | MatTableModule, 38 | BrowserAnimationsModule, 39 | NgxSpinnerModule, 40 | ], 41 | providers: [], 42 | bootstrap: [MainComponent], 43 | }) 44 | export class AppModule {} 45 | -------------------------------------------------------------------------------- /packages/angular-charts/scaffolding/src/app/main/main.component.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /packages/angular-charts/scaffolding/src/app/main/main.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, HostListener, OnInit } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | import { getDependencies, getCodesandboxFiles } from '../../code-chunks'; 5 | 6 | window['__cubejsPlayground'] = { 7 | getDependencies, 8 | getCodesandboxFiles, 9 | }; 10 | 11 | @Component({ 12 | selector: 'app-root', 13 | templateUrl: './main.component.html', 14 | styles: [], 15 | }) 16 | export class MainComponent implements OnInit { 17 | chartingLibrary = null; 18 | 19 | query$ = new BehaviorSubject({}); 20 | pivotConfig$ = new BehaviorSubject(null); 21 | chartType$ = new BehaviorSubject('line'); 22 | 23 | ngOnInit() { 24 | const queryId = window.location.hash.replace(/#\\/, '').split('=')[1]; 25 | const { forQuery } = window.parent.window['__cubejsPlayground'] || {}; 26 | const { onChartRendererReady } = forQuery(queryId); 27 | if (typeof onChartRendererReady === 'function') { 28 | onChartRendererReady(); 29 | } 30 | } 31 | 32 | @HostListener('window:__cubejsPlaygroundEvent', ['$event']) 33 | onCubejsEvent(event: any) { 34 | const { 35 | query, 36 | pivotConfig, 37 | chartType, 38 | chartingLibrary, 39 | eventType, 40 | } = event.detail; 41 | 42 | if (eventType === 'chart') { 43 | if (query !== undefined) { 44 | this.query$.next(query); 45 | } 46 | if (pivotConfig !== undefined) { 47 | this.pivotConfig$.next(pivotConfig); 48 | } 49 | if (chartType) { 50 | this.chartType$.next(chartType); 51 | } 52 | if (chartingLibrary) { 53 | this.chartingLibrary = chartingLibrary; 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/angular-charts/scaffolding/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /packages/angular-charts/scaffolding/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | export const environment = { 5 | production: false, 6 | }; 7 | /* 8 | * For easier debugging in development mode, you can import the following file 9 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 10 | * 11 | * This import should be commented out in production mode because it will have a negative impact 12 | * on performance if an error is thrown. 13 | */ 14 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 15 | -------------------------------------------------------------------------------- /packages/angular-charts/scaffolding/src/styles.css: -------------------------------------------------------------------------------- 1 | @import "~@angular/material/prebuilt-themes/indigo-pink.css"; 2 | 3 | * { 4 | font-family: BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, 5 | sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 6 | font-weight: 300; 7 | } 8 | -------------------------------------------------------------------------------- /packages/angular-ng2-charts/index.js: -------------------------------------------------------------------------------- 1 | const { TemplatePackage } = require('@cubejs-templates/core'); 2 | 3 | class Ng2ChartsLibraryTemplate extends TemplatePackage { 4 | importDependencies() { 5 | return { 6 | 'chart.js': '^2.9.4', 7 | }; 8 | } 9 | } 10 | 11 | module.exports = (context) => new Ng2ChartsLibraryTemplate(context); 12 | -------------------------------------------------------------------------------- /packages/angular-ng2-charts/scaffolding/app/query-renderer/query-renderer.component.html: -------------------------------------------------------------------------------- 1 |
2 |
6 | 15 | 16 | 17 | 26 | 27 | 28 | 37 | 38 | 39 | 48 | 49 |
50 | 51 | 57 | 61 | 64 | 65 | 66 | 67 | 68 | 69 |
62 | {{ columnTitles[index] }} 63 | {{ element[column] }}
70 | 71 |
72 |

73 | {{ value | number }} 74 |

75 |
76 |
77 | -------------------------------------------------------------------------------- /packages/angular-ng2-charts/scaffolding/app/query-renderer/utils.ts: -------------------------------------------------------------------------------- 1 | import { TableColumn } from '@cubejs-client/core'; 2 | 3 | export function getDisplayedColumns(tableColumns: TableColumn[]): string[] { 4 | const queue = tableColumns; 5 | const columns = []; 6 | 7 | while (queue.length) { 8 | const column = queue.shift(); 9 | if (column.dataIndex) { 10 | columns.push(column.dataIndex); 11 | } 12 | if ((column.children || []).length) { 13 | column.children.map((child) => queue.push(child)); 14 | } 15 | } 16 | 17 | return columns; 18 | } 19 | 20 | export function flattenColumns(columns: TableColumn[] = []) { 21 | return columns.reduce((memo, column) => { 22 | const titles = flattenColumns(column.children); 23 | return [ 24 | ...memo, 25 | ...(titles.length 26 | ? titles.map((title) => column.shortTitle + ', ' + title) 27 | : [column.shortTitle]), 28 | ]; 29 | }, []); 30 | } 31 | -------------------------------------------------------------------------------- /packages/antd-tables/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | TemplatePackage, 3 | ChartRendererSnippet, 4 | } = require('@cubejs-templates/core'); 5 | 6 | class AntdTablesTemplate extends TemplatePackage {} 7 | 8 | module.exports = (context) => 9 | new AntdTablesTemplate(context, { 10 | '/src/components/ChartRenderer.js': new ChartRendererSnippet(), 11 | }); 12 | -------------------------------------------------------------------------------- /packages/antd-tables/scaffolding/src/components/ChartRenderer.js: -------------------------------------------------------------------------------- 1 | import { Row, Col, Statistic, Table } from 'antd'; 2 | import { useDeepCompareMemo } from 'use-deep-compare'; 3 | 4 | const formatTableData = (columns, data) => { 5 | function flatten(columns = []) { 6 | return columns.reduce((memo, column) => { 7 | if (column.children) { 8 | return [...memo, ...flatten(column.children)]; 9 | } 10 | 11 | return [...memo, column]; 12 | }, []); 13 | } 14 | 15 | const typeByIndex = flatten(columns).reduce((memo, column) => { 16 | return { 17 | ...memo, 18 | [column.dataIndex]: column, 19 | }; 20 | }, {}); 21 | 22 | function formatValue(value, { type, format } = {}) { 23 | if (value == undefined) { 24 | return value; 25 | } 26 | 27 | if (type === 'boolean') { 28 | if (typeof value === 'boolean') { 29 | return value.toString(); 30 | } else if (typeof value === 'number') { 31 | return Boolean(value).toString(); 32 | } 33 | 34 | return value; 35 | } 36 | 37 | if (type === 'number' && format === 'percent') { 38 | return [parseFloat(value).toFixed(2), '%'].join(''); 39 | } 40 | 41 | return value.toString(); 42 | } 43 | 44 | function format(row) { 45 | return Object.fromEntries( 46 | Object.entries(row).map(([dataIndex, value]) => { 47 | return [dataIndex, formatValue(value, typeByIndex[dataIndex])]; 48 | }) 49 | ); 50 | } 51 | 52 | return data.map(format); 53 | }; 54 | 55 | const TableRenderer = ({ resultSet, pivotConfig }) => { 56 | const [tableColumns, dataSource] = useDeepCompareMemo(() => { 57 | const columns = resultSet.tableColumns(pivotConfig); 58 | 59 | return [ 60 | columns, 61 | formatTableData(columns, resultSet.tablePivot(pivotConfig)), 62 | ]; 63 | }, [resultSet, pivotConfig]); 64 | 65 | return ( 66 | 67 | ); 68 | }; 69 | 70 | const TypeToChartComponent = { 71 | number: ({ resultSet }) => { 72 | return ( 73 | 79 | 80 | {resultSet.seriesNames().map((s) => ( 81 | 82 | ))} 83 | 84 | 85 | ); 86 | }, 87 | table: ({ resultSet, pivotConfig }) => { 88 | return ; 89 | }, 90 | }; 91 | -------------------------------------------------------------------------------- /packages/bizcharts-charts/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | TemplatePackage, 3 | ChartRendererSnippet, 4 | } = require('@cubejs-templates/core'); 5 | 6 | class BizchartTemplate extends TemplatePackage { 7 | importDependencies() { 8 | return { 9 | bizcharts: '4.1.7', 10 | }; 11 | } 12 | } 13 | 14 | module.exports = (context) => 15 | new BizchartTemplate(context, { 16 | '/src/components/ChartRenderer.js': new ChartRendererSnippet(), 17 | }); 18 | -------------------------------------------------------------------------------- /packages/chartjs-charts/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | TemplatePackage, 3 | ChartRendererSnippet, 4 | } = require('@cubejs-templates/core'); 5 | 6 | class ChartjsTemplate extends TemplatePackage { 7 | importDependencies() { 8 | return { 9 | 'react-chartjs-2': '^3.0.3', 10 | 'chart.js': '^3.4.0', 11 | }; 12 | } 13 | } 14 | 15 | module.exports = (context) => 16 | new ChartjsTemplate(context, { 17 | '/src/components/ChartRenderer.js': new ChartRendererSnippet(), 18 | }); 19 | -------------------------------------------------------------------------------- /packages/create-ng-app/index.js: -------------------------------------------------------------------------------- 1 | const { TemplatePackage } = require('../templates-core'); 2 | const fs = require('fs-extra'); 3 | const path = require('path'); 4 | const isDocker = require('is-docker'); 5 | 6 | class CreateNgAppTemplate extends TemplatePackage { 7 | async onBeforeApply() { 8 | const isInstalled = this.appContainer.getPackageVersions()[this.name]; 9 | 10 | if (!isInstalled) { 11 | if (!isDocker()) { 12 | await this.appContainer 13 | .executeCommand( 14 | 'npx', 15 | [ 16 | '@angular/cli', 17 | 'new', 18 | path.basename(this.appContainer.appPath), 19 | '--routing=false', 20 | '--style=css', 21 | '--minimal=true', 22 | '--packageManager=npm', 23 | '--strict=false', 24 | ], 25 | { 26 | cwd: path.dirname(this.appContainer.appPath), 27 | } 28 | ) 29 | .catch((e) => { 30 | if (e.toString().indexOf('ENOENT') !== -1) { 31 | throw new Error( 32 | `npx is not installed. Please update your npm: \`$ npm install -g npm\`.` 33 | ); 34 | } 35 | throw e; 36 | }); 37 | } else { 38 | // a workaround to avoid `Error: Cannot find module '../node_modules/@angular/cli/bin/postinstall/script.js'` until it gets fixed 39 | await this.appContainer.executeCommand( 40 | 'echo n | npm install -g @angular/cli', 41 | [], 42 | { 43 | shell: true, 44 | } 45 | ); 46 | await this.appContainer.executeCommand( 47 | 'ng', 48 | [ 49 | 'new', 50 | path.basename(this.appContainer.appPath), 51 | '--routing=false', 52 | '--style=css', 53 | '--minimal=true', 54 | '--packageManager=npm', 55 | '--strict=false', 56 | ], 57 | { 58 | cwd: path.dirname(this.appContainer.appPath), 59 | } 60 | ); 61 | } 62 | } 63 | } 64 | 65 | async onAfterApply() { 66 | const appPath = path.resolve(this.appContainer.appPath); 67 | const content = fs.readFileSync(`${appPath}/tsconfig.json`, 'utf-8'); 68 | 69 | if (content) { 70 | try { 71 | const json = JSON.parse(content.replace(/\/\*(.*)\*\//, '')); 72 | json.compilerOptions.allowSyntheticDefaultImports = true; 73 | fs.writeFileSync( 74 | `${appPath}/tsconfig.json`, 75 | JSON.stringify(json, null, 2) 76 | ); 77 | } catch (error) { 78 | console.error(`Cannot parse 'tsconfig.json`, error); 79 | } 80 | } 81 | } 82 | } 83 | 84 | module.exports = (context) => new CreateNgAppTemplate(context); 85 | -------------------------------------------------------------------------------- /packages/create-react-app/index.js: -------------------------------------------------------------------------------- 1 | const { spawnSync } = require('child_process'); 2 | 3 | const { TemplatePackage } = require('../templates-core'); 4 | 5 | class CreateReactAppTemplate extends TemplatePackage { 6 | importDependencies() { 7 | return { 8 | react: '16.14.0', 9 | 'react-dom': '16.14.0', 10 | }; 11 | } 12 | 13 | async onBeforeApply() { 14 | const isInstalled = this.appContainer.getPackageVersions()[this.name]; 15 | 16 | if (!isInstalled) { 17 | try { 18 | await this.appContainer.executeCommand('npx', [ 19 | 'create-react-app', 20 | this.appContainer.appPath, 21 | '--use-npm', 22 | ]); 23 | } catch (error) { 24 | if (error.toString().indexOf('ENOENT') !== -1) { 25 | throw new Error( 26 | `npx is not installed. Please update your npm: \`$ npm install -g npm\`.` 27 | ); 28 | } 29 | 30 | const child1 = spawnSync('create-react-app --version', { shell: true }); 31 | const child2 = spawnSync('npm view create-react-app version', { 32 | shell: true, 33 | }); 34 | 35 | if ( 36 | child1.stdout.toString() !== '' && 37 | child2.stdout.toString() !== child1.stdout.toString() 38 | ) { 39 | throw new Error( 40 | 'Create React App does not longer support global installation.\n\n' + 41 | 'Please remove any global installs with one of the following commands:\n' + 42 | '- npm uninstall -g create-react-app\n' + 43 | '- yarn global remove create-react-app\n\n' 44 | ); 45 | } 46 | } 47 | } 48 | } 49 | 50 | async onBeforePersist(sourceContainer) { 51 | sourceContainer.fileContent['.env'] = 'SKIP_PREFLIGHT_CHECK=true'; 52 | sourceContainer.fileContent['src/index.js'] = `import React from 'react'; 53 | import ReactDOM from 'react-dom'; 54 | import App from './App'; 55 | 56 | ReactDOM.render( 57 | 58 | 59 | , 60 | document.getElementById('root') 61 | );`; 62 | } 63 | } 64 | 65 | module.exports = (context) => new CreateReactAppTemplate(context); 66 | -------------------------------------------------------------------------------- /packages/create-vue-app/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | 4 | const { TemplatePackage } = require('../templates-core'); 5 | 6 | class CreateNgAppTemplate extends TemplatePackage { 7 | importDependencies() { 8 | return { 9 | 'node-sass': '^5.0.0', 10 | sass: '^1.32.8', 11 | 'sass-loader': '^10.1.1', 12 | }; 13 | } 14 | 15 | async onBeforeApply() { 16 | const isInstalled = this.appContainer.getPackageVersions()[this.name]; 17 | 18 | if (!isInstalled) { 19 | try { 20 | await this.appContainer.executeCommand( 21 | `npx @vue/cli create -m npm -n -d ${path.basename( 22 | this.appContainer.appPath 23 | )}`, 24 | [], 25 | { 26 | cwd: path.dirname(this.appContainer.appPath), 27 | shell: true, 28 | } 29 | ); 30 | } catch (error) { 31 | if (error.toString().indexOf('ENOENT') !== -1) { 32 | throw new Error( 33 | `npx is not installed. Please update your npm: \`$ npm install -g npm\`.` 34 | ); 35 | } 36 | throw error; 37 | } 38 | } 39 | } 40 | 41 | async onAfterApply() { 42 | const appPath = path.resolve(this.appContainer.appPath); 43 | 44 | try { 45 | await this.appContainer.executeCommand( 46 | 'rm src/components/HelloWorld.vue 2> /dev/null', 47 | [], 48 | { 49 | cwd: appPath, 50 | shell: true, 51 | } 52 | ); 53 | await this.appContainer.executeCommand('rm src/main.js', [], { 54 | cwd: appPath, 55 | shell: true, 56 | }); 57 | } catch (e) { 58 | console.log(e); 59 | } 60 | } 61 | 62 | async onBeforePersist() { 63 | const packageJsonPath = path.join( 64 | this.appContainer.appPath, 65 | 'package.json' 66 | ); 67 | 68 | const json = fs.readJsonSync(packageJsonPath); 69 | json.scripts.start = 'vue-cli-service serve'; 70 | 71 | fs.writeFileSync(packageJsonPath, JSON.stringify(json)); 72 | } 73 | } 74 | 75 | module.exports = (context) => new CreateNgAppTemplate(context); 76 | -------------------------------------------------------------------------------- /packages/d3-charts/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | TemplatePackage, 3 | ChartRendererSnippet, 4 | } = require('@cubejs-templates/core'); 5 | 6 | class D3Template extends TemplatePackage { 7 | importDependencies() { 8 | return { 9 | d3: '6.5.0', 10 | }; 11 | } 12 | } 13 | 14 | module.exports = (context) => 15 | new D3Template(context, { 16 | '/src/components/ChartRenderer.js': new ChartRendererSnippet(), 17 | }); 18 | -------------------------------------------------------------------------------- /packages/dev-cnga/index.js: -------------------------------------------------------------------------------- 1 | const { TemplatePackage } = require('@cubejs-templates/core'); 2 | 3 | class DevCraTemplate extends TemplatePackage { 4 | async onBeforeApply() { 5 | try { 6 | await this.appContainer.executeCommand('cp', [ 7 | '-R', 8 | `${__dirname}/scaffolding`, 9 | this.appContainer.appPath, 10 | ]); 11 | } catch (e) { 12 | if (e.toString().indexOf('ENOENT') !== -1) { 13 | throw new Error( 14 | `npx is not installed. Please update your npm: \`$ npm install -g npm\`.` 15 | ); 16 | } 17 | throw e; 18 | } 19 | } 20 | } 21 | 22 | module.exports = (context) => new DevCraTemplate(context); 23 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/.angular.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [ 3 | { 4 | "root": "src", 5 | "outDir": "dist", 6 | "assets": ["assets", "favicon.ico"], 7 | "index": "index.html", 8 | "main": "main.ts", 9 | "polyfills": "polyfills.ts", 10 | "prefix": "app", 11 | "styles": ["styles.css"], 12 | "scripts": [], 13 | "environmentSource": "environments/environment.ts", 14 | "environments": { 15 | "dev": "environments/environment.ts", 16 | "prod": "environments/environment.prod.ts" 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "test", 3 | "author": "Cube Dev, Inc.", 4 | "license": "MIT", 5 | "devDependencies": { 6 | "husky": "^4.2.5", 7 | "prettier": "^2.0.5", 8 | "pretty-quick": "^2.0.1" 9 | }, 10 | "husky": { 11 | "hooks": { 12 | "pre-commit": "pretty-quick --staged" 13 | } 14 | }, 15 | "dependencies": { 16 | "@cubejs-templates/core": "latest" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
chart type: {{ chartType | async | json }}
2 | 3 | 8 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'], 7 | }) 8 | export class AppComponent { 9 | title = 'CodeSandbox'; 10 | } 11 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { CubejsClientModule } from '@cubejs-client/ngx'; 4 | import { ChartsModule } from 'ng2-charts'; 5 | 6 | import { MainComponent } from './main/main.component'; 7 | import { AppComponent } from './app.component'; 8 | import { Ng2ChartRendererComponent } from './ng2-chart-renderer/chart-renderer.component'; 9 | import { RechartsChartRendererComponent } from './recharts-chart-renderer/chart-renderer.component'; 10 | 11 | const cubejsOptions = { 12 | token: 'CUBEJS_TOKEN', 13 | options: { 14 | apiUrl: `http://localhost:4000/cubejs-api/v1`, 15 | }, 16 | }; 17 | 18 | @NgModule({ 19 | declarations: [ 20 | MainComponent, 21 | AppComponent, 22 | Ng2ChartRendererComponent, 23 | RechartsChartRendererComponent, 24 | ], 25 | imports: [ 26 | BrowserModule, 27 | CubejsClientModule.forRoot(cubejsOptions), 28 | ChartsModule, 29 | ], 30 | providers: [], 31 | bootstrap: [MainComponent], 32 | }) 33 | export class AppModule {} 34 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cube-js/cube-playground-templates/365141994ebb4b76f4e31686d04b7d5cfd545047/packages/dev-cnga/scaffolding/src/favicon.ico -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularCharts 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | // function dispatchChartEvent(detail) { 12 | // const myEvent = new CustomEvent('cubejs', { 13 | // bubbles: true, // bubble event to containing elements 14 | // composed: true, // let the event pass through the shadowDOM boundary 15 | // detail 16 | // }); 17 | 18 | // window.dispatchEvent(myEvent); 19 | // } 20 | 21 | // window['dispatchChartEvent'] = dispatchChartEvent; 22 | 23 | platformBrowserDynamic() 24 | .bootstrapModule(AppModule) 25 | .catch((err) => console.error(err)); 26 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | import 'document-register-element'; 64 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /packages/dev-cnga/scaffolding/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "target": "es2015", 13 | "module": "es2020", 14 | "lib": ["es2018", "dom"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/dev-cra/index.js: -------------------------------------------------------------------------------- 1 | const { TemplatePackage } = require('@cubejs-templates/core'); 2 | 3 | class DevCraTemplate extends TemplatePackage { 4 | async onBeforeApply() { 5 | try { 6 | await this.appContainer.executeCommand('cp', [ 7 | '-R', 8 | `${__dirname}/scaffolding`, 9 | this.appContainer.appPath, 10 | ]); 11 | } catch (e) { 12 | if (e.toString().indexOf('ENOENT') !== -1) { 13 | throw new Error( 14 | `npx is not installed. Please update your npm: \`$ npm install -g npm\`.` 15 | ); 16 | } 17 | throw e; 18 | } 19 | } 20 | } 21 | 22 | module.exports = (context) => new DevCraTemplate(context); 23 | -------------------------------------------------------------------------------- /packages/dev-cra/scaffolding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "test", 3 | "author": "Cube Dev, Inc.", 4 | "license": "MIT", 5 | "devDependencies": { 6 | "husky": "^4.2.5", 7 | "prettier": "^2.0.5", 8 | "pretty-quick": "^2.0.1" 9 | }, 10 | "husky": { 11 | "hooks": { 12 | "pre-commit": "pretty-quick --staged" 13 | } 14 | }, 15 | "dependencies": { 16 | "@cubejs-templates/core": "latest" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/dev-cra/scaffolding/src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f0f2f5 !important; 3 | } 4 | -------------------------------------------------------------------------------- /packages/dev-cra/scaffolding/src/App.js: -------------------------------------------------------------------------------- 1 | import logo from './logo.svg'; 2 | import './App.css'; 3 | 4 | function App() { 5 | return ( 6 |
7 |
8 | logo 9 |

10 | Edit src/App.js and save to reload. 11 |

12 | 18 | Learn React 19 | 20 |
21 |
22 | ); 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /packages/dev-cra/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/dev-cva/index.js: -------------------------------------------------------------------------------- 1 | const { TemplatePackage } = require('@cubejs-templates/core'); 2 | 3 | class DevCvaTemplate extends TemplatePackage { 4 | async onBeforeApply() { 5 | try { 6 | await this.appContainer.executeCommand('cp', [ 7 | '-R', 8 | `${__dirname}/scaffolding`, 9 | this.appContainer.appPath, 10 | ]); 11 | } catch (e) { 12 | if (e.toString().indexOf('ENOENT') !== -1) { 13 | throw new Error( 14 | `npx is not installed. Please update your npm: \`$ npm install -g npm\`.` 15 | ); 16 | } 17 | throw e; 18 | } 19 | } 20 | } 21 | 22 | module.exports = (context) => new DevCvaTemplate(context); 23 | -------------------------------------------------------------------------------- /packages/dev-cva/scaffolding/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/cli-plugin-babel/preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/dev-cva/scaffolding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-vue", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "vue": "^2.6.11" 13 | }, 14 | "devDependencies": { 15 | "@vue/cli-plugin-babel": "~4.5.0", 16 | "@vue/cli-plugin-eslint": "~4.5.0", 17 | "@vue/cli-service": "~4.5.0", 18 | "babel-eslint": "^10.1.0", 19 | "eslint": "^6.7.2", 20 | "eslint-plugin-vue": "^6.2.2", 21 | "vue-template-compiler": "^2.6.11" 22 | }, 23 | "eslintConfig": { 24 | "root": true, 25 | "env": { 26 | "node": true 27 | }, 28 | "extends": [ 29 | "plugin:vue/essential", 30 | "eslint:recommended" 31 | ], 32 | "parserOptions": { 33 | "parser": "babel-eslint" 34 | }, 35 | "rules": {} 36 | }, 37 | "browserslist": [ 38 | "> 1%", 39 | "last 2 versions", 40 | "not dead" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /packages/dev-cva/scaffolding/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/dev-cva/scaffolding/src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 29 | -------------------------------------------------------------------------------- /packages/dev-cva/scaffolding/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App.vue'; 3 | 4 | Vue.config.productionTip = false; 5 | 6 | new Vue({ 7 | render: (h) => h(App), 8 | }).$mount('#app'); 9 | -------------------------------------------------------------------------------- /packages/material-tables/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | TemplatePackage, 3 | ChartRendererSnippet, 4 | } = require('@cubejs-templates/core'); 5 | 6 | class MaterialTablesTemplate extends TemplatePackage {} 7 | 8 | module.exports = (context) => 9 | new MaterialTablesTemplate(context, { 10 | '/src/components/ChartRenderer.js': new ChartRendererSnippet(), 11 | }); 12 | -------------------------------------------------------------------------------- /packages/material-tables/scaffolding/src/components/ChartRenderer.js: -------------------------------------------------------------------------------- 1 | import Typography from '@material-ui/core/Typography'; 2 | import Table from '@material-ui/core/Table'; 3 | import TableBody from '@material-ui/core/TableBody'; 4 | import TableCell from '@material-ui/core/TableCell'; 5 | import TableHead from '@material-ui/core/TableHead'; 6 | import TableRow from '@material-ui/core/TableRow'; 7 | 8 | const TypeToChartComponent = { 9 | number: ({ resultSet }) => { 10 | return ( 11 | 17 | {resultSet.seriesNames().map((s) => resultSet.totalRow()[s.key])} 18 | 19 | ); 20 | }, 21 | table: ({ resultSet }) => { 22 | return ( 23 |
24 | 25 | 26 | {resultSet.tableColumns().map((c) => ( 27 | {c.title} 28 | ))} 29 | 30 | 31 | 32 | {resultSet.tablePivot().map((row, index) => ( 33 | 34 | {resultSet.tableColumns().map((c) => ( 35 | {row[c.key]} 36 | ))} 37 | 38 | ))} 39 | 40 |
41 | ); 42 | }, 43 | }; 44 | -------------------------------------------------------------------------------- /packages/ng-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/app.module.ts': new CredentialsSnippet( 15 | context.playgroundContext.credentials 16 | ), 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/ng-credentials/scaffolding/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | const API_URL = undefined; 2 | const CUBEJS_TOKEN = undefined; 3 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/index.js: -------------------------------------------------------------------------------- 1 | const { TemplatePackage } = require('@cubejs-templates/core'); 2 | 3 | class NgMaterialDynamicTemplate extends TemplatePackage { 4 | importDependencies() { 5 | return { 6 | '@apollo/client': 'latest', 7 | graphql: '15.7.2', 8 | 'graphql-tools': '5.0.0', 9 | }; 10 | } 11 | } 12 | 13 | module.exports = (context) => new NgMaterialDynamicTemplate(context); 14 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { DashboardComponent } from './dashboard/dashboard.component'; 5 | import { ExploreComponent } from './explore/explore.component'; 6 | 7 | const routes: Routes = [ 8 | { path: '', redirectTo: 'explore', pathMatch: 'full' }, 9 | { path: 'explore', component: ExploreComponent }, 10 | { path: 'dashboard', component: DashboardComponent }, 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [RouterModule.forRoot(routes)], 15 | exports: [RouterModule], 16 | }) 17 | export class AppRoutingModule {} 18 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | }) 7 | export class AppComponent { 8 | constructor() {} 9 | } 10 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/dashboard/dashboard.component.css: -------------------------------------------------------------------------------- 1 | .no-items { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 |
2 |

There are no charts on this dashboard

3 | 4 | Add Chart 7 |
8 | 9 |
13 | 14 | 18 | 23 |
24 | 33 | 34 | 37 | 38 | 39 |
40 |
41 |
42 |
43 |
44 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/add-to-dashboard-dialog/add-to-dashboard-dialog.component.css: -------------------------------------------------------------------------------- 1 | .form-field { 2 | display: block; 3 | } 4 | 5 | .action-buttons { 6 | display: flex; 7 | align-content: center; 8 | justify-content: flex-end; 9 | } 10 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/add-to-dashboard-dialog/add-to-dashboard-dialog.component.html: -------------------------------------------------------------------------------- 1 |

Save Chart

2 | 3 |
4 |
5 | 6 | Name 7 | 8 | 13 | This field is mandatory. 16 | 17 | 18 |
19 | 20 |
21 | 24 | 27 |
28 |
29 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/add-to-dashboard-dialog/add-to-dashboard-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject } from '@angular/core'; 2 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 3 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 4 | import { Router } from '@angular/router'; 5 | import { Apollo, gql } from 'apollo-angular'; 6 | 7 | @Component({ 8 | selector: 'add-to-dashboard-dialog', 9 | templateUrl: './add-to-dashboard-dialog.component.html', 10 | styleUrls: ['./add-to-dashboard-dialog.component.css'], 11 | }) 12 | export class AddToDashboardDialogComponent { 13 | chartForm: FormGroup; 14 | 15 | constructor( 16 | public dialogRef: MatDialogRef, 17 | @Inject(MAT_DIALOG_DATA) public data: any, 18 | private formBuilder: FormBuilder, 19 | private apollo: Apollo, 20 | private router: Router 21 | ) {} 22 | 23 | ngOnInit() { 24 | this.chartForm = this.formBuilder.group({ 25 | name: ['New Chart', Validators.required], 26 | }); 27 | } 28 | 29 | submit() { 30 | if (!this.chartForm.valid) { 31 | return; 32 | } 33 | 34 | let mutation; 35 | const { itemId } = this.data; 36 | 37 | if (itemId) { 38 | mutation = gql` 39 | mutation updateDashboardItem($id: String!, $input: DashboardItemInput) { 40 | updateDashboardItem(id: $id, input: $input) { 41 | id 42 | name 43 | } 44 | } 45 | `; 46 | } else { 47 | mutation = gql` 48 | mutation createDashboardItem($input: DashboardItemInput) { 49 | createDashboardItem(input: $input) { 50 | id 51 | name 52 | } 53 | } 54 | `; 55 | } 56 | 57 | this.apollo 58 | .mutate({ 59 | mutation, 60 | variables: { 61 | ...(itemId ? { id: itemId.toString() } : {}), 62 | input: { 63 | name: this.chartForm.value.name, 64 | vizState: JSON.stringify({ 65 | query: this.data.cubeQuery, 66 | chartType: this.data.chartType, 67 | pivotConfig: this.data.pivotConfig, 68 | }), 69 | layout: '', 70 | }, 71 | }, 72 | }) 73 | .subscribe(() => { 74 | this.dialogRef.close(); 75 | this.router.navigate(['/dashboard']); 76 | }); 77 | } 78 | 79 | onNoClick(): void { 80 | this.dialogRef.close(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/explore.component.css: -------------------------------------------------------------------------------- 1 | .explore-container { 2 | display: flex; 3 | min-height: 100vh; 4 | flex-direction: column; 5 | } 6 | 7 | .controls-container { 8 | display: grid; 9 | gap: 16px; 10 | } 11 | 12 | .controls-row { 13 | display: flex; 14 | flex-wrap: wrap; 15 | gap: 16px; 16 | } 17 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/filter-group/filter-group.component.css: -------------------------------------------------------------------------------- 1 | .filter-member-container { 2 | padding-bottom: 12px; 3 | display: flex; 4 | align-items: center; 5 | } 6 | 7 | .filter-input { 8 | width: 200px; 9 | } 10 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/filter-group/filter-group.component.html: -------------------------------------------------------------------------------- 1 |
2 | 11 | 12 | 20 |
21 | 22 | 23 | add 24 | Filter 25 | 29 | No members found 30 | 31 | {{ member.title }} 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/filter-group/filter-group.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | EventEmitter, 4 | Input, 5 | OnDestroy, 6 | OnInit, 7 | Output, 8 | } from '@angular/core'; 9 | import { FormControl, Validators } from '@angular/forms'; 10 | import { MatSelect, MatSelectChange } from '@angular/material/select'; 11 | import { FilterMember } from '@cubejs-client/ngx'; 12 | import { BehaviorSubject } from 'rxjs'; 13 | import { debounceTime } from 'rxjs/operators'; 14 | 15 | @Component({ 16 | selector: 'filter-group', 17 | templateUrl: './filter-group.component.html', 18 | styleUrls: ['./filter-group.component.css'], 19 | }) 20 | export class FilterGroupComponent implements OnInit, OnDestroy { 21 | currentFilter = new BehaviorSubject(null); 22 | 23 | @Input() 24 | filters: FilterMember; 25 | 26 | @Input() 27 | members: any[]; 28 | 29 | @Input() 30 | allMembers: any[]; 31 | 32 | ngOnInit(): void { 33 | this.currentFilter.subscribe((filter) => { 34 | if (filter?.operator) { 35 | this.filters.add({ 36 | member: filter.name, 37 | operator: filter.operator, 38 | values: filter.values || [''], 39 | }); 40 | 41 | this.currentFilter.next(null); 42 | } 43 | }); 44 | } 45 | 46 | ngOnDestroy() { 47 | this.currentFilter.unsubscribe(); 48 | } 49 | 50 | selectMember(event: MatSelect) { 51 | this.currentFilter.next({ 52 | ...this.allMembers.find(({ name }) => event.value === name), 53 | values: [], 54 | }); 55 | } 56 | 57 | handleOperatorChange(operator: MatSelect) { 58 | this.currentFilter.next({ 59 | ...this.currentFilter.value, 60 | operator, 61 | }); 62 | } 63 | 64 | handleValueChange(value: string) { 65 | this.currentFilter.next({ 66 | ...this.currentFilter.value, 67 | values: [value], 68 | }); 69 | } 70 | 71 | trackByMethod(_, member: any) { 72 | return member?.name; 73 | } 74 | } 75 | 76 | @Component({ 77 | selector: 'filter', 78 | templateUrl: './filter.component.html', 79 | styleUrls: ['./filter-group.component.css'], 80 | }) 81 | export class FilterComponent implements OnInit { 82 | private _member: any; 83 | 84 | @Input() 85 | set member(member) { 86 | const nextValue = member?.values[0] || ''; 87 | if (nextValue !== this.filterValue.value) { 88 | this.filterValue.setValue(nextValue); 89 | } 90 | this._member = member; 91 | } 92 | 93 | get member() { 94 | return this._member; 95 | } 96 | 97 | @Input() 98 | allMembers: any[]; 99 | 100 | @Output() 101 | onOperatorSelect = new EventEmitter(); 102 | 103 | @Output() 104 | valueChanges = new EventEmitter(); 105 | 106 | @Output() 107 | onRemove = new EventEmitter(); 108 | 109 | @Output() 110 | selectMember = new EventEmitter(); 111 | 112 | filterValue = new FormControl('', [Validators.required]); 113 | 114 | ngOnInit() { 115 | this.filterValue.valueChanges.pipe(debounceTime(300)).subscribe((value) => { 116 | this.valueChanges.emit(value); 117 | }); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/filter-group/filter.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | Filter 5 | 9 | 10 | {{ member.title }} 11 | 12 | 13 | 14 | 15 | 24 |
25 | 26 | 27 | Operator 28 | 32 | 36 | {{ operator.title }} 37 | 38 | 39 | 40 | 41 | 42 | Filter value 43 | 44 | Filter value is requred 45 | 46 |
47 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/members-group/members-group.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cube-js/cube-playground-templates/365141994ebb4b76f4e31686d04b7d5cfd545047/packages/ng-material-dynamic/scaffolding/src/app/explore/members-group/members-group.component.css -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/members-group/members-group.component.html: -------------------------------------------------------------------------------- 1 |
2 |
6 | 7 | {{ title }} 8 | 12 | 13 | {{ member.title }} 14 | 15 | 16 | 17 | 18 | 21 |
22 | 23 | 27 | add 28 | {{ title }} 29 | 33 | No members found 34 | 35 | {{ member.title }} 36 | 37 | 38 | 39 |
40 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/members-group/members-group.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, Output } from '@angular/core'; 2 | import { MatSelectChange } from '@angular/material/select'; 3 | 4 | @Component({ 5 | selector: 'members-group', 6 | templateUrl: './members-group.component.html', 7 | styleUrls: ['./members-group.component.css'], 8 | }) 9 | export class MembersGroupComponent { 10 | @Input() 11 | title: string; 12 | 13 | @Input() 14 | members: any[]; 15 | 16 | @Input() 17 | allMembers: any[]; 18 | 19 | @Output() 20 | onSelect = new EventEmitter(); 21 | 22 | @Output() 23 | onMemberClick = new EventEmitter(); 24 | 25 | @Output() 26 | onReplace = new EventEmitter(); 27 | 28 | _onSelect(event: MatSelectChange) { 29 | this.onSelect.emit(event.value); 30 | } 31 | 32 | _onMemberClick(name: string) { 33 | this.onMemberClick.emit(name); 34 | } 35 | 36 | _onReplace(name: string, replaceWithName: string) { 37 | this.onReplace.emit({ 38 | name, 39 | replaceWithName, 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/order/order.component.css: -------------------------------------------------------------------------------- 1 | .item { 2 | display: flex; 3 | justify-content: space-between; 4 | padding: 8px 3px; 5 | cursor: grab; 6 | } 7 | 8 | .cdk-drag-placeholder { 9 | opacity: 0; 10 | } 11 | 12 | .cdk-drag-animating { 13 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 14 | } 15 | 16 | .drag-item { 17 | display: flex; 18 | align-items: center; 19 | cursor: grab; 20 | } 21 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/order/order.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | drag_indicator 5 | {{ orderMember.title }} 6 |
7 | 8 | 15 |
16 |
17 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/order/order.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | import { CdkDragDrop } from '@angular/cdk/drag-drop'; 3 | import type { Order, TOrder, TOrderMember } from '@cubejs-client/ngx'; 4 | 5 | @Component({ 6 | selector: 'app-order', 7 | templateUrl: './order.component.html', 8 | styleUrls: ['./order.component.css'], 9 | }) 10 | export class OrderComponent implements OnInit { 11 | @Input() 12 | order: Order; 13 | 14 | ngOnInit(): void {} 15 | 16 | drop(event: CdkDragDrop) { 17 | this.order.reorder(event.previousIndex, event.currentIndex); 18 | } 19 | 20 | changeOrder(orderMember: TOrderMember) { 21 | const orderOptions: TOrder[] = ['asc', 'desc', 'none']; 22 | 23 | function getNextOrder(order: TOrder): TOrder { 24 | const index = orderOptions.indexOf(order) + 1; 25 | return orderOptions[index > 2 ? 0 : index]; 26 | } 27 | 28 | this.order.setMemberOrder(orderMember.id, getNextOrder(orderMember.order)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/pivot/pivot.component.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: grid; 3 | grid-template-columns: 1fr auto 1fr; 4 | } 5 | 6 | .axis { 7 | padding: 8px 16px; 8 | } 9 | 10 | .axis-name { 11 | font-weight: 600; 12 | text-align: center; 13 | } 14 | 15 | .drag-item { 16 | display: flex; 17 | align-items: center; 18 | cursor: grab; 19 | } 20 | 21 | .cdk-drag-placeholder { 22 | opacity: 0; 23 | } 24 | 25 | .cdk-drag-animating { 26 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 27 | } 28 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/pivot/pivot.component.html: -------------------------------------------------------------------------------- 1 |
2 |
12 |
x
13 | 14 |
15 | drag_indicator {{ id }} 16 |
17 |
18 | 19 | 20 | 21 |
31 |
y
32 | 33 |
34 | drag_indicator {{ id }} 35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/pivot/pivot.component.ts: -------------------------------------------------------------------------------- 1 | import { CdkDragDrop } from '@angular/cdk/drag-drop'; 2 | import { Component, Input, OnInit } from '@angular/core'; 3 | import { TSourceAxis } from '@cubejs-client/core'; 4 | import type { PivotConfig } from '@cubejs-client/ngx'; 5 | 6 | @Component({ 7 | selector: 'app-pivot', 8 | templateUrl: './pivot.component.html', 9 | styleUrls: ['./pivot.component.css'], 10 | }) 11 | export class PivotComponent implements OnInit { 12 | @Input() 13 | pivotConfig: PivotConfig; 14 | x = []; 15 | y = []; 16 | 17 | ngOnInit() { 18 | this.pivotConfig.subject.subscribe(({ x, y }) => { 19 | this.x = x; 20 | this.y = y; 21 | }); 22 | } 23 | 24 | drop(event: CdkDragDrop) { 25 | this.pivotConfig.moveItem( 26 | event.previousIndex, 27 | event.currentIndex, 28 | event.previousContainer.id as TSourceAxis, 29 | event.container.id as TSourceAxis 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/query-renderer/query-renderer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | import { MatDialog } from '@angular/material/dialog'; 3 | import { MatSnackBar } from '@angular/material/snack-bar'; 4 | import { 5 | isQueryPresent, 6 | PivotConfig as TPivotConfig, 7 | Query, 8 | ResultSet, 9 | } from '@cubejs-client/core'; 10 | import { CubejsClient, TChartType } from '@cubejs-client/ngx'; 11 | import { combineLatest, Observable, of } from 'rxjs'; 12 | import { catchError, switchMap } from 'rxjs/operators'; 13 | 14 | @Component({ 15 | selector: 'query-renderer', 16 | templateUrl: './query-renderer.component.html', 17 | styleUrls: ['./query-renderer.component.css'], 18 | }) 19 | export class QueryRendererComponent implements OnInit { 20 | data: any = {}; 21 | isQueryPresent: boolean; 22 | 23 | @Input() 24 | resetResultSetOnChange: boolean = false; 25 | 26 | @Input('cubeQuery') 27 | cubeQuery$: Observable; 28 | 29 | @Input('pivotConfig') 30 | pivotConfig$: Observable; 31 | 32 | @Input('chartType') 33 | chartType$: Observable; 34 | 35 | chartType: TChartType; 36 | 37 | constructor( 38 | private cubejsClient: CubejsClient, 39 | private snakBar: MatSnackBar, 40 | private dialog: MatDialog 41 | ) {} 42 | 43 | ngOnInit() { 44 | combineLatest([ 45 | this.cubeQuery$.pipe( 46 | switchMap((cubeQuery) => { 47 | if (!isQueryPresent(cubeQuery || {})) { 48 | return of(null); 49 | } 50 | this.data.cubeQuery = cubeQuery; 51 | return this.cubejsClient.load(cubeQuery).pipe( 52 | catchError((error) => { 53 | this.snakBar.open(error.message || 'Request error', null, { 54 | duration: 2000, 55 | }); 56 | return of(null); 57 | }) 58 | ); 59 | }) 60 | ), 61 | this.pivotConfig$, 62 | this.chartType$, 63 | ]).subscribe( 64 | ([resultSet, pivotConfig, chartType]: [ 65 | ResultSet, 66 | TPivotConfig, 67 | TChartType 68 | ]) => { 69 | this.chartType = chartType; 70 | this.data.chartType = chartType; 71 | this.data.pivotConfig = pivotConfig; 72 | this.isQueryPresent = resultSet != null; 73 | this.updateChart(resultSet, pivotConfig); 74 | } 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/time-group/time-group.component.html: -------------------------------------------------------------------------------- 1 |
2 | 9 | 10 | 11 | 12 | FOR 13 | 14 | 18 | 22 | {{ dateRange.title }} 23 | 24 | 25 | 26 | 27 | 28 | BY 29 | 30 | 34 | 35 | {{ g.title }} 36 | 37 | 38 | 39 | 40 |
41 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/explore/time-group/time-group.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; 2 | import { MatSelectChange } from '@angular/material/select'; 3 | import { TimeDimensionMember } from '@cubejs-client/ngx'; 4 | 5 | @Component({ 6 | selector: 'time-group', 7 | templateUrl: './time-group.component.html', 8 | }) 9 | export class TimeGroupComponent { 10 | granularities = [ 11 | { value: '', title: 'w/o grouping' }, 12 | { value: 'hour', title: 'Hour' }, 13 | { value: 'day', title: 'Day' }, 14 | { value: 'week', title: 'Week' }, 15 | { value: 'month', title: 'Month' }, 16 | { value: 'year', title: 'Year' }, 17 | ]; 18 | dateRanges = [ 19 | { title: 'All time' }, 20 | { title: 'Today' }, 21 | { title: 'Yesterday' }, 22 | { title: 'This week' }, 23 | { title: 'This month' }, 24 | { title: 'This quarter' }, 25 | { title: 'This year' }, 26 | { title: 'Last 7 days' }, 27 | { title: 'Last 30 days' }, 28 | { title: 'Last week' }, 29 | { title: 'Last month' }, 30 | { title: 'Last quarter' }, 31 | { title: 'Last year' }, 32 | ]; 33 | 34 | @Input() 35 | timeDimensionMember: TimeDimensionMember; 36 | 37 | @Input() 38 | members: any; 39 | 40 | @Input() 41 | allMembers: any[]; 42 | 43 | @Output() 44 | onDateRangeSelect: EventEmitter = new EventEmitter(); 45 | 46 | @Output() 47 | onGranularitySelect: EventEmitter = new EventEmitter(); 48 | 49 | get dateRange() { 50 | return this.timeDimensionMember.asArray()[0]?.dateRange; 51 | } 52 | 53 | get granularity() { 54 | return this.timeDimensionMember.asArray()[0]?.granularity; 55 | } 56 | 57 | handleTimeDimensionSelect(value: string) { 58 | this.timeDimensionMember.add(value); 59 | } 60 | 61 | handleDateRangeSelect(event: MatSelectChange) { 62 | this.timeDimensionMember.setDateRange( 63 | 0, 64 | event.value === 'All time' ? undefined : event.value 65 | ); 66 | } 67 | 68 | handleGranularitySelect(event: MatSelectChange) { 69 | this.timeDimensionMember.setGranularity(0, event.value || undefined); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/settings-dialog/settings-dialog.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Fill Missing Dates 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/app/settings-dialog/settings-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject } from '@angular/core'; 2 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 3 | 4 | @Component({ 5 | selector: 'app-settings-dialog', 6 | templateUrl: './settings-dialog.component.html', 7 | }) 8 | export class SettingsDialogComponent { 9 | constructor( 10 | public dialogRef: MatDialogRef, 11 | @Inject(MAT_DIALOG_DATA) public data: any 12 | ) {} 13 | 14 | onNoClick(): void { 15 | this.dialogRef.close(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/graphql/client.ts: -------------------------------------------------------------------------------- 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).toString()); 21 | return currentId.toString(); 22 | }; 23 | 24 | const toApolloItem = (i) => ({ ...i, __typename: 'DashboardItem' }); 25 | 26 | const typeDefs = ` 27 | type DashboardItem { 28 | id: String! 29 | layout: String 30 | vizState: String 31 | name: String 32 | } 33 | 34 | input DashboardItemInput { 35 | layout: String 36 | vizState: String 37 | name: String 38 | } 39 | 40 | type Query { 41 | dashboardItems: [DashboardItem] 42 | dashboardItem(id: String!): DashboardItem 43 | } 44 | 45 | type Mutation { 46 | createDashboardItem(input: DashboardItemInput): DashboardItem 47 | updateDashboardItem(id: String!, input: DashboardItemInput): DashboardItem 48 | deleteDashboardItem(id: String!): DashboardItem 49 | } 50 | `; 51 | const schema = makeExecutableSchema({ 52 | typeDefs, 53 | resolvers: { 54 | Query: { 55 | dashboardItems() { 56 | const dashboardItems = getDashboardItems(); 57 | 58 | return dashboardItems.map(toApolloItem); 59 | }, 60 | 61 | dashboardItem(_, { id }) { 62 | const dashboardItems = getDashboardItems(); 63 | return toApolloItem(dashboardItems.find((i) => i.id.toString() === id)); 64 | }, 65 | }, 66 | Mutation: { 67 | createDashboardItem: (_, { input: { ...item } }) => { 68 | const dashboardItems = getDashboardItems(); 69 | item = { ...item, id: nextId(), layout: JSON.stringify({}) }; 70 | dashboardItems.push(item); 71 | setDashboardItems(dashboardItems); 72 | 73 | return toApolloItem(item); 74 | }, 75 | updateDashboardItem: (_, { id, input: { ...item } }) => { 76 | const dashboardItems = getDashboardItems(); 77 | item = Object.keys(item) 78 | .filter((k) => !!item[k]) 79 | .map((k) => ({ 80 | [k]: item[k], 81 | })) 82 | .reduce((a, b) => ({ ...a, ...b }), {}); 83 | const index = dashboardItems.findIndex((i) => i.id.toString() === id); 84 | dashboardItems[index] = { ...dashboardItems[index], ...item }; 85 | setDashboardItems(dashboardItems); 86 | 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 | 95 | return toApolloItem(removedItem); 96 | }, 97 | }, 98 | }, 99 | }); 100 | 101 | export default new ApolloClient({ 102 | cache, 103 | link: new SchemaLink({ 104 | schema, 105 | }), 106 | }); 107 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dynamic Material UI Angular Dashboard 6 | 7 | 8 | 9 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/ng-material-dynamic/scaffolding/src/styles.css: -------------------------------------------------------------------------------- 1 | @import "@angular/material/prebuilt-themes/deeppurple-amber.css"; 2 | 3 | html, 4 | body { 5 | height: 100%; 6 | } 7 | body { 8 | margin: 0; 9 | font-family: Roboto, "Helvetica Neue", sans-serif; 10 | } 11 | 12 | .mat-tab-label { 13 | min-width: 80px !important; 14 | } 15 | 16 | .mat-form-field-infix { 17 | width: 100px !important; 18 | } 19 | 20 | .no-padding-controls .mat-form-field-wrapper { 21 | padding-bottom: 0 !important; 22 | } 23 | 24 | .no-padding-controls .mat-form-field-underline { 25 | bottom: 0 !important; 26 | } 27 | 28 | .mat-tab-body-wrapper { 29 | padding: 24px 16px !important; 30 | } 31 | 32 | .auto-width .mat-form-field-infix, 33 | .auto-width .mat-form-field { 34 | width: auto !important; 35 | } 36 | 37 | .auto-width .mat-select-value { 38 | max-width: 100% !important; 39 | width: auto !important; 40 | } 41 | 42 | .filters-container .selected-member-container .mat-form-field { 43 | width: 100%; 44 | } 45 | 46 | .flex-box { 47 | display: flex; 48 | gap: 8px; 49 | align-items: flex-end; 50 | } 51 | 52 | .selected-member-container { 53 | display: flex; 54 | align-items: center; 55 | } 56 | -------------------------------------------------------------------------------- /packages/ng2-charts/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | TemplatePackage, 3 | QueryRendererSnippet, 4 | } = require('@cubejs-templates/core'); 5 | 6 | class Ng2ChartsTemplate extends TemplatePackage { 7 | importDependencies() { 8 | return { 9 | 'chart.js': '3.7.0', 10 | 'ng2-charts': '3.0.6', 11 | }; 12 | } 13 | } 14 | 15 | module.exports = (context) => 16 | new Ng2ChartsTemplate(context, { 17 | '/src/app/explore/query-renderer/query-renderer.component.ts': new QueryRendererSnippet(), 18 | }); 19 | -------------------------------------------------------------------------------- /packages/ng2-charts/scaffolding/src/app/explore/query-renderer/query-renderer.component.css: -------------------------------------------------------------------------------- 1 | .chart-container { 2 | position: relative; 3 | height: calc(100% - 52px); 4 | min-height: 400px; 5 | } 6 | 7 | .action-button-wrapper { 8 | display: flex; 9 | justify-content: flex-end; 10 | margin-top: 12px; 11 | } 12 | 13 | :host { 14 | position: absolute; 15 | top: 0; 16 | bottom: 0; 17 | left: 0; 18 | right: 0; 19 | } 20 | -------------------------------------------------------------------------------- /packages/ng2-charts/scaffolding/src/app/explore/query-renderer/query-renderer.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |
7 | 8 |
9 | 18 | 19 | 20 | 29 | 30 | 31 | 40 | 41 | 42 | 51 | 52 |
53 | 54 | 60 | 64 | 67 | 68 | 69 | 70 | 71 | 72 |
65 | {{ columnTitles[index] }} 66 | {{ element[column] }}
73 |
74 | -------------------------------------------------------------------------------- /packages/ng2-charts/scaffolding/src/app/explore/query-renderer/query-renderer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | import { MatDialog } from '@angular/material/dialog'; 3 | import { MatSnackBar } from '@angular/material/snack-bar'; 4 | import { 5 | PivotConfig as TPivotConfig, 6 | Query, 7 | ResultSet, 8 | } from '@cubejs-client/core'; 9 | import { CubejsClient, TChartType } from '@cubejs-client/ngx'; 10 | import { ChartDataset, ChartOptions } from 'chart.js'; 11 | import { Observable } from 'rxjs'; 12 | 13 | import { AddToDashboardDialogComponent } from '../add-to-dashboard-dialog/add-to-dashboard-dialog.component'; 14 | import { flattenColumns, getDisplayedColumns } from './utils'; 15 | 16 | @Component({ 17 | selector: 'query-renderer', 18 | templateUrl: './query-renderer.component.html', 19 | styleUrls: ['./query-renderer.component.css'], 20 | }) 21 | export class QueryRendererComponent implements OnInit { 22 | displayedColumns: string[] = []; 23 | tableData: any[] = []; 24 | columnTitles: string[] = []; 25 | chartData: ChartDataset[] = []; 26 | chartLabels: any[] = []; 27 | chartOptions: ChartOptions = { 28 | responsive: true, 29 | maintainAspectRatio: false, 30 | }; 31 | noFillChartOptions: ChartOptions = { 32 | responsive: true, 33 | maintainAspectRatio: false, 34 | elements: { 35 | line: { 36 | fill: false, 37 | }, 38 | }, 39 | }; 40 | 41 | updateChart(resultSet: ResultSet | null, pivotConfig: TPivotConfig) { 42 | if (!resultSet) { 43 | return; 44 | } 45 | 46 | if (this.chartType === 'table') { 47 | this.tableData = resultSet.tablePivot(pivotConfig); 48 | this.displayedColumns = getDisplayedColumns( 49 | resultSet.tableColumns(pivotConfig) 50 | ); 51 | this.columnTitles = flattenColumns(resultSet.tableColumns(pivotConfig)); 52 | } else { 53 | this.chartData = resultSet.series(pivotConfig).map((item) => { 54 | return { 55 | label: item.title, 56 | data: item.series.map(({ value }) => value), 57 | stack: 'a', 58 | }; 59 | }); 60 | this.chartLabels = resultSet.chartPivot(pivotConfig).map((row) => row.x); 61 | } 62 | } 63 | 64 | openDialog(): void { 65 | const dialogRef = this.dialog.open(AddToDashboardDialogComponent, { 66 | width: '500px', 67 | data: this.data, 68 | }); 69 | 70 | dialogRef.updatePosition({ 71 | top: '10%', 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /packages/ng2-charts/scaffolding/src/app/explore/query-renderer/utils.ts: -------------------------------------------------------------------------------- 1 | import { TableColumn } from '@cubejs-client/core'; 2 | 3 | export function getDisplayedColumns(tableColumns: TableColumn[]): string[] { 4 | const queue = tableColumns; 5 | const columns = []; 6 | 7 | while (queue.length) { 8 | const column = queue.shift(); 9 | if (column.dataIndex) { 10 | columns.push(column.dataIndex); 11 | } 12 | if ((column.children || []).length) { 13 | column.children.map((child) => queue.push(child)); 14 | } 15 | } 16 | 17 | return columns; 18 | } 19 | 20 | export function flattenColumns(columns: TableColumn[] = []) { 21 | return columns.reduce((memo, column) => { 22 | const titles = flattenColumns(column.children); 23 | return [ 24 | ...memo, 25 | ...(titles.length 26 | ? titles.map((title) => column.shortTitle + ', ' + title) 27 | : [column.shortTitle]), 28 | ]; 29 | }, []); 30 | } 31 | -------------------------------------------------------------------------------- /packages/react-antd-dynamic/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | TemplatePackage, 3 | AppSnippet, 4 | IndexSnippet, 5 | } = require('@cubejs-templates/core'); 6 | 7 | class ReactAntdDynamicTemplate extends TemplatePackage { 8 | importDependencies() { 9 | return { 10 | graphql: '15.7.2', 11 | 'graphql-tools': '5.0.0', 12 | 'react-router': '5.2.1', 13 | 'react-router-dom': '5.2.1', 14 | '@apollo/react-hooks': '3.1.3', 15 | }; 16 | } 17 | } 18 | 19 | module.exports = (context) => 20 | new ReactAntdDynamicTemplate(context, { 21 | '/src/App.js': new AppSnippet(), 22 | '/src/index.js': new IndexSnippet(), 23 | }); 24 | -------------------------------------------------------------------------------- /packages/react-antd-dynamic/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 { ApolloProvider } from '@apollo/react-hooks'; 6 | import { Layout } from 'antd'; 7 | import cubejs from '@cubejs-client/core'; 8 | import { CubeProvider } from '@cubejs-client/react'; 9 | import client from './graphql/client'; 10 | import Header from './components/Header'; 11 | 12 | const API_URL = undefined; 13 | 14 | const CUBEJS_TOKEN = undefined; 15 | 16 | const cubejsApi = cubejs(CUBEJS_TOKEN, { apiUrl: `${API_URL}/cubejs-api/v1` }); 17 | 18 | const AppLayout = ({ children }) => ( 19 | 24 |
25 | {children} 26 | 27 | ); 28 | 29 | const App = ({ children }) => ( 30 | 31 | 32 | {children} 33 | 34 | 35 | ); 36 | 37 | export default App; 38 | -------------------------------------------------------------------------------- /packages/react-antd-dynamic/scaffolding/src/body.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f0f2f5 !important; 3 | } 4 | 5 | .ant-popover-disabled-compatible-wrapper { 6 | pointer-events: none; 7 | } 8 | -------------------------------------------------------------------------------- /packages/react-antd-dynamic/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, pivotConfig }) => 13 | (resultSet && ( 14 | 15 | )) || 16 | (error && error.toString()) || ; 17 | 18 | const ChartRenderer = ({ vizState }) => { 19 | const { query, chartType, pivotConfig } = vizState; 20 | const component = TypeToMemoChartComponent[chartType]; 21 | const renderProps = useCubeQuery(query); 22 | 23 | return component && renderChart(component)({ ...renderProps, pivotConfig }); 24 | }; 25 | 26 | ChartRenderer.propTypes = { 27 | vizState: PropTypes.object, 28 | cubejsApi: PropTypes.object, 29 | }; 30 | 31 | ChartRenderer.defaultProps = { 32 | vizState: {}, 33 | cubejsApi: null, 34 | }; 35 | 36 | export default ChartRenderer; 37 | -------------------------------------------------------------------------------- /packages/react-antd-dynamic/scaffolding/src/components/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RGL, { WidthProvider } from 'react-grid-layout'; 3 | import { useMutation } from '@apollo/react-hooks'; 4 | import 'react-grid-layout/css/styles.css'; 5 | import 'react-resizable/css/styles.css'; 6 | import { GET_DASHBOARD_ITEMS } from '../graphql/queries'; 7 | import { UPDATE_DASHBOARD_ITEM } from '../graphql/mutations'; 8 | 9 | const ReactGridLayout = WidthProvider(RGL); 10 | 11 | const Dashboard = ({ children, dashboardItems }) => { 12 | const [updateDashboardItem] = useMutation(UPDATE_DASHBOARD_ITEM, { 13 | refetchQueries: [ 14 | { 15 | query: GET_DASHBOARD_ITEMS, 16 | }, 17 | ], 18 | }); 19 | 20 | const onLayoutChange = (newLayout) => { 21 | newLayout.forEach((l) => { 22 | const item = dashboardItems.find((i) => i.id.toString() === l.i); 23 | const toUpdate = JSON.stringify({ 24 | x: l.x, 25 | y: l.y, 26 | w: l.w, 27 | h: l.h, 28 | }); 29 | 30 | if (item && toUpdate !== item.layout) { 31 | updateDashboardItem({ 32 | variables: { 33 | id: item.id, 34 | input: { 35 | layout: toUpdate, 36 | }, 37 | }, 38 | }); 39 | } 40 | }); 41 | }; 42 | 43 | return ( 44 | 45 | {children} 46 | 47 | ); 48 | }; 49 | 50 | export default Dashboard; 51 | -------------------------------------------------------------------------------- /packages/react-antd-dynamic/scaffolding/src/components/DashboardItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, Menu, Button, Dropdown, Modal } from 'antd'; 3 | import { useMutation } from '@apollo/react-hooks'; 4 | import { Link } from 'react-router-dom'; 5 | import { Icon } from '@ant-design/compatible'; 6 | import { GET_DASHBOARD_ITEMS } from '../graphql/queries'; 7 | import { DELETE_DASHBOARD_ITEM } from '../graphql/mutations'; 8 | 9 | const DashboardItemDropdown = ({ itemId }) => { 10 | const [removeDashboardItem] = useMutation(DELETE_DASHBOARD_ITEM, { 11 | refetchQueries: [ 12 | { 13 | query: GET_DASHBOARD_ITEMS, 14 | }, 15 | ], 16 | }); 17 | const dashboardItemDropdownMenu = ( 18 | 19 | 20 | Edit 21 | 22 | 24 | Modal.confirm({ 25 | title: 'Are you sure you want to delete this item?', 26 | okText: 'Yes', 27 | okType: 'danger', 28 | cancelText: 'No', 29 | 30 | onOk() { 31 | removeDashboardItem({ 32 | variables: { 33 | id: itemId, 34 | }, 35 | }); 36 | }, 37 | }) 38 | } 39 | > 40 | Delete 41 | 42 | 43 | ); 44 | return ( 45 | 50 | 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 | 19 | {ChartTypes.map((m) => ( 20 | updateChartType(m.name)}> 21 | 22 | {m.title} 23 | 24 | ))} 25 | 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 | 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 | 38 | 39 | Dashboard 40 | 41 | 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 | 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 | 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 | 34 | 109 | -------------------------------------------------------------------------------- /packages/vue-chartkick-charts/scaffolding/src/components/Table.vue: -------------------------------------------------------------------------------- 1 | 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 | 20 | 21 | 128 | -------------------------------------------------------------------------------- /packages/vue-charts/scaffolding/src/ChartRenderer.vue: -------------------------------------------------------------------------------- 1 | 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 | 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 | 10 | 11 | 54 | -------------------------------------------------------------------------------- /packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/DateRangeSelect.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 46 | -------------------------------------------------------------------------------- /packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/MeasureSetter.vue: -------------------------------------------------------------------------------- 1 | 11 | 20 | -------------------------------------------------------------------------------- /packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/TimeDimensionSelect.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/dialogs/AddToDashboard.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 51 | -------------------------------------------------------------------------------- /packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/dialogs/Limit.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 47 | -------------------------------------------------------------------------------- /packages/vue-vuetify-dynamic/scaffolding/src/pages/explore/components/dialogs/Order.vue: -------------------------------------------------------------------------------- 1 | 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 | 9 | 10 | 37 | --------------------------------------------------------------------------------