UNPKG

sugar-generate

Version:

Auto generate OAS 3.0 REST + GraphQL APIs (Node + MongoDB)

364 lines (339 loc) 11 kB
const fs = require('fs'); const beautify = require('js-beautify').html; const { uppercase } = require('../api/utils'); const uuid = require('uuid').v4; module.exports = async ({ schema, destination }) => { const getReactState = require('../app/components/getReactState'); const textFieldsByType = require('../app/components/textFieldsByType'); const getTableColumns = require('../app/components/getTableColumns'); const componentID = uuid(); const fileName = `${destination}/${uppercase(schema.name)}/${uppercase(schema.name)}Table.html`; const componentName = `${uppercase(schema.name)}Table` console.log('FILENAME', fileName) const host = "http://localhost:8080" const code = [ ` <!DOCTYPE html> <html lang="en"> <head> <title>${componentName}</title> <meta charset="utf-8" /> <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no" /> <script src="https://unpkg.com/react@latest/umd/react.development.js" crossorigin="anonymous"></script> <script src="https://unpkg.com/react-dom@latest/umd/react-dom.development.js"></script> <script src="https://unpkg.com/@material-ui/core@latest/umd/material-ui.development.js" crossorigin="anonymous"></script> <!-- Fonts to support Material Design --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /> <!-- Icons to support Material Design --> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" /> <script src="https://unpkg.com/zoid@9.0.27/dist/zoid.frameworks.min.js"></script> <script src="https://unpkg.com/clsx@1.0.4/dist/clsx.min.js"></script> <script src="https://unpkg.com/query-string@6.7.0/index.js"></script> <script src="https://unpkg.com/@material-ui/icons@4.1.0/index.js"></script> <script src="https://unpkg.com/material-table@1.39.0/dist/index.js"></script> <script src="https://unpkg.com/babel-standalone@latest/babel.min.js" crossorigin="anonymous"></script> <script src="${host}/${componentName}.js"></script> </head> <body> <div id="container"></div> <script type="text/babel"> console.log('table', window._MaterialTable) // mui defs const { colors, CssBaseline, MuiThemeProvider, Typography, Container, makeStyles, createMuiTheme, Box, SvgIcon, Link, withStyles, FormGroup, TextField, Button, Snackbar, SnackbarContent, FormControlLabel, FormControl, FormLabel, RadioGroup, Radio, Switch, } = MaterialUI; const tableIcons = { Add: window.AddBox, Check: window.Check, Clear: window.Clear, Delete: window.DeleteOutline, DetailPanel: window.ChevronRight, Edit: window.Edit, Export: window.SaveAlt, Filter: window.FilterList, FirstPage: window.FirstPage, LastPage: window.LastPage, NextPage: window.ChevronRight, PreviousPage: window.ChevronLeft, ResetSearch: window.Clear, Search: window.Search, SortArrow: window.ArrowUpward, ThirdStateCheck: window.Remove, ViewColumn: window.ViewColumn, Refresh: window.Refresh, }; const apiCall = ({ apiURL, url, method, data, params = null, fullURL = null, }) => { // Default options are marked with * let stringified = ""; if (params) { Object.keys(params).forEach((param) => { if (param === 'orderBy' && params[param]) { const orderBy = {}; orderBy[params[param].field] = params.orderDirection; delete params.orderDirection; delete params.orderBy; params.sort = JSON.stringify(orderBy); } if (typeof params[param] === 'object') params[param] = JSON.stringify(params[param]); }) stringified = \`?\${queryString.stringify(params)}\`; } const fetchObject = { headers: { 'Content-Type': 'application/json', // 'Content-Type': 'application/x-www-form-urlencoded', // Authorization? }, // credentials: 'include', // include, *same-origin, omit cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached mode: 'cors', // no-cors, cors, *same-origin method: 'GET', // *GET, POST, PUT, DELETE, etc. redirect: 'follow', // manual, *follow, error referrer: 'no-referrer', // no-referrer, *client } if (method) fetchObject.method = method; if (data) fetchObject.body = JSON.stringify(data); return fetch(fullURL ? fullURL : \`\${apiURL}/\${url}\${stringified}\`, fetchObject).then(response => response.json()); } // Create a theme instance. const theme = createMuiTheme({ palette: { primary: { main: '#556cd6', }, secondary: { main: '#19857b', }, error: { main: colors.red.A400, }, background: { default: '#fff', }, }, }); const styles = { root: { margin: theme.spacing(6, 0, 3), }, lightBulb: { verticalAlign: 'middle', marginRight: theme.spacing(1), }, container: { display: 'flex', flexWrap: 'wrap', maxWidth: 600, alignItems: 'center', justifyContent: 'center', margin: '0 auto', }, fields: { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', alignItems: "flex-start", justifyContent: "space-between", }, form: { // flexDirection: 'column', // display: 'flex', maxWidth: 600, alignItems: 'center', justifyContent: 'center', }, radioGroup: { flexDirection: 'row', display: 'flex', }, switch: { // width: 200, // minHeight: 56, // margin: '16px 8px 8px 8px', }, switchRoot: { root: { width: 42, height: 26, padding: 0, margin: theme.spacing(1), }, }, radioRoot: { width: 200, minHeight: 56, margin: '16px 8px 8px 8px', }, switchLabel: { display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', width: 200, minHeight: 56, margin: '16px 8px 8px 8px', }, button: { margin: theme.spacing(1), }, textField: { marginLeft: theme.spacing(1), marginRight: theme.spacing(1), width: 200, }, dense: { marginTop: 19, }, menu: { width: 200, }, }; class ${componentName} extends React.Component { static getInitialProps = () => { return style = {}; } constructor(props) { super(props); this.state = ${getReactState(schema, true)} this.state.snackbar = { variant: 'success', message: '', } this.state.docs = []; this.state.limit = 10; this.state.totalDocs = 0; this.state.offset = 0; this.schema = ${JSON.stringify(schema.schema)} } componentWillMount () { this.fetchData(); } fetchData = async (query) => { if (!query) query = {}; query.limit = query.pageSize || 10; const res = await apiCall({ url: '${schema.name}s', method: 'GET', params: query }); return { data: res.${schema.name}s.docs, page: res.${schema.name}s.offset / res.${schema.name}s.limit, totalCount: res.${schema.name}s.totalDocs, } } onSnackbarClose = (e) => { this.setState({ snackbar: { message: '', variant: 'info' }}) } onRowAdd = async (newData) => { const res = await apiCall({ url: "${schema.name}", method: 'POST', data: newData }); if (!res.error) this.setState({ snackbar: { variant: 'success', message: '${uppercase(schema.name)} Created.' }}); else this.setState({ snackbar: { variant: 'error', message: res.error }}); } onRowUpdate = async (newData, oldData) => { const res = await apiCall({ url: \`${schema.name}/\${oldData._id}\`, method: 'PUT', data: newData }); if (!res.error) this.setState({ snackbar: { variant: 'success', message: '${uppercase(schema.name)} Updated.' }}); else this.setState({ snackbar: { variant: 'error', message: res.error }}); } onRowDelete = async (oldData) => { const res = await apiCall({ url: \`${schema.name}/\${oldData._id}\`, method: 'DELETE', }) if (!res.error) this.setState({ snackbar: { variant: 'success', message: '${uppercase(schema.name)} Deleted.' }}); else this.setState({ snackbar: { variant: 'error', message: res.error }}); } render () { /* Cant edit / delete when selection: true https://github.com/mbrn/material-table/issues/648 */ const { classes } = this.props; const { snackbar, ${getReactState(schema, true, true)} } = this.state; return ( <div className={classes.container}> <div className={classes.table}> <div style={{ maxWidth: "100%" }}> <MaterialTable columns={${getTableColumns(schema)}} data={this.fetchData} icons={tableIcons} title="${uppercase(schema.name)} Table" options={{ filtering: true, selection: false, exportButton: true }} tableRef={this.tableRef} localization={{ body: { editRow: { deleteText: 'Delete this row?' } } }} editable={{ onRowAdd: this.onRowAdd, onRowUpdate: this.onRowUpdate, onRowDelete: this.onRowDelete, isEditable: (rowData) => { // console.log('ROWDATA', rowData) // const areUnique = []; // const columns = ${getTableColumns(schema)}; // console.log('COLUMNS', columns) // columns.forEach((column) => column.readonly ? areUnique.push(column.field) : ""); // console.log('AREUNIQUE', areUnique) return true; }, isDeletable: () => true, }} actions={[ { icon: Refresh, tooltip: 'Refresh Data', isFreeAction: true, onClick: () => this.tableRef.current && this.tableRef.current.onQueryChange(), } ]} /> </div> </div> <Snackbar variant={snackbar.variant} message={snackbar.message} handleClose={this.onSnackbarClose} /> </div> ); } } const ${componentName}WithStyles = withStyles(styles, { withTheme: true })(${componentName}) ReactDOM.render( <MuiThemeProvider theme={theme}> {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */} <CssBaseline /> <${componentName}WithStyles { ...window.xprops }/> </MuiThemeProvider>, document.querySelector('#container'), ); </script> </body> </html> `]; const pretty = beautify(code.join("\n"), { indent_size: 2, space_in_empty_paren: true }); fs.writeFileSync(fileName, pretty); };