Alan Colon před 7 roky
rodič
revize
5e4a7de963

+ 46 - 25
app/crud/pages/details.js

@@ -6,6 +6,8 @@ const { dollarIcon } = require('../../assets')
  */
 const details = (opts) => {
 
+  const hideCriteria = column => column.routeParam ? `ctrl.$routeParams.${column.routeParam} !== '${opts.paramAll}'` : 'false'
+
   const autocompleteInput = column => {
     if (!column.apiPrefix) throw new Error('apiPrefix is required for autocomplete fields')
 
@@ -19,7 +21,7 @@ const details = (opts) => {
         md-selected-item-change="ctrl.autocomplete.${raw(column.camelName)}.onChange()"
         md-min-length="0"s
         placeholder="${column.titleName}"
-        >
+        ng-hide="${hideCriteria(column)}">
         <md-item-template>
           <span md-highlight-text="ctrl.searchText.${raw(column.camelName)}" md-highlight-flags="^i">{{item.name || item.key || item.id}}</span>
         </md-item-template>
@@ -30,14 +32,14 @@ const details = (opts) => {
   const attrs = obj => obj ? _.toPairs().map((key, value) => html`${key}="${value}"`).join(' ') : ''
 
   const standardInput = column => html`
-    <md-input-container flex>
+    <md-input-container flex ng-hide="${hideCriteria(column)}">
       <label>${column.titleName}</label>
       <input type="${column.type || 'text'}" ${attrs(column.attrs)} ng-model="model.${raw(column.camelName)}" />
     </md-input-container>
   `
 
   const currencyInput = column => html`
-    <md-input-container flex>
+    <md-input-container flex ng-hide="${hideCriteria(column)}">
       <label>${column.titleName}</label>
       <input type="number" step="0.01" ng-model="model.${raw(column.camelName)}" />
       <md-icon md-svg-src="${dollarIcon}"></md-icon>
@@ -56,32 +58,46 @@ const details = (opts) => {
       const cols = _.fromPairs(opts.columns.map(col => [col.camelName, col]))
 
       return opts.layout.map(section => html`
-        ${section.section ? html`<md-subheader>${section.section}</md-subheader>`:''}
-        ${raw(section.rows ? section.rows.map(
-          row => html`
-            <div layout-gt-sm="row" layout-padding>
-              ${raw(row
-                .map(field => cols[field])
-                .map(c => c.field || defaultField(c))
-                .join('\n')
-              )}
-            </div>
-          `).join('\n')
-        : '')}
-      `).join('\n')
+        <md-card>
+          ${section.section ? html`<h2 style="padding-left: 15px">${section.section}</h2>`:''}
+          <md-card-content layout="column" layout-margin>
+          ${raw(section.rows ? section.rows.map(
+            row => html`
+              <div flex layout="column" layout-gt-sm="row" layout-margin>
+                ${raw(row
+                  .map(field => cols[field])
+                  .map(c => c.field || defaultField(c))
+                  .join('\n')
+                )}
+              </div>
+            `).join('\n')
+          : '')}
+          </md-card-content>
+        </md-card>`).join('\n')
     } else {
       return html`
-        <div layout-gt-sm="row" layout-padding>
-          ${opts.columns.map(c => c.field || defaultField(c))}
-        </div>
+        <md-card>
+          <md-card-content layout="column" layout-gt-sm="column" layout-margin>
+          ${opts.columns.map(c => html`
+            ${c.field || defaultField(c)}
+          `)}
+          </md-card-content>
+        </md-card>
       `
     }
   }
 
   const template = html`
       <app-user-area>
-        <h1>{{ctrl.isNew ? 'New ${opts.titleName}' : '${opts.titleName} Details'}}</h1>
-        <form name="form" ng-submit="ctrl.submit()">
+        <h1 ng-show="ctrl.isNew">New ${opts.titleName}</h1>
+        <h1 ng-hide="ctrl.isNew">${opts.titleName} Details</h1>
+        <form name="form" ng-submit="ctrl.submit()" layout="column" layout-margin
+          flex-xs="100"
+          flex-sm="100"
+          flex-md="70"
+          flex-lg="70"
+          flex-xl="70"
+        >
           ${raw(layout())}
           <div>
             <md-button type="submit" class="md-raised md-primary">Submit</md-button>
@@ -95,10 +111,15 @@ const details = (opts) => {
 
     template,
     controllerAs: 'ctrl',
-    controller: function(api, $scope, $routeParams, $mdToast, $location, $q) {
+    controller: function(api, $scope, $routeParams, $mdToast, $location, $q, util) {
+      this.$routeParams = $routeParams
       this.template = template // For inspection purposes
+
+      this.apiPrefix = util.fillPath(opts.apiPrefix, $routeParams)
+      this.appPrefix = util.fillPath(opts.appPrefix, $routeParams)
+
       this.isNew = $routeParams[opts.routeParam] === opts.paramNew
-      const crud = api.crud(opts.apiPrefix)
+      const crud = api.crud(this.apiPrefix)
       let original
       if (this.isNew) {
         original = {}
@@ -126,7 +147,7 @@ const details = (opts) => {
             await crud.update(original.id, obj)
           }
           $mdToast.showSimple(`${opts.titleName} saved.`)
-          $location.url(opts.appPrefix)
+          $location.url(this.appPrefix)
         } catch (err) {
           console.error(err)
           $mdToast.showSimple(`Could not save ${opts.titleName}: ${err.message || err.statusText || err}`)
@@ -138,7 +159,7 @@ const details = (opts) => {
       this.autocomplete = {}
 
       opts.columns.filter(c => c.type === 'autocomplete').forEach(c => {
-        const crud = api.crud(c.apiPrefix)
+        const crud = api.crud(util.fillPath(c.apiPrefix, $routeParams))
         const ac = this.autocomplete[c.camelName] = {
           onChange() {
             $scope.model[c.camelName] = ac.selectedItem

+ 17 - 9
app/crud/pages/list.js

@@ -5,12 +5,15 @@ const {editIcon, createIcon, deleteIcon} = require('../../assets')
  * @param {CrudPagesOptions} opts 
  */
 const list = (opts) => {
-  const defaultHeader = column => html`<th md-column>${column.titleName}</th>`
+  const hideCriteria = column => column.routeParam ? `ctrl.$routeParams.${column.routeParam} !== '${opts.paramAll}'` : 'false'
+
+  const defaultHeader = column =>
+    html`<th ng-hide="${hideCriteria(column)}" md-column>${column.titleName}</th>`
   const defaultCell = column => {
     if (column.type === 'autocomplete') {
-      return html`<td md-cell>{{${raw(opts.camelName)}.${raw(column.camelName)} && (ctrl.autocomplete.${raw(column.camelName)}.lookup[${raw(opts.camelName)}.${raw(column.camelName)}] || ctrl.autocomplete.${raw(column.camelName)}.load(${raw(opts.camelName)}.${raw(column.camelName)}))}}</td>`
+      return html`<td ng-hide="${hideCriteria(column)}" md-cell>{{${raw(opts.camelName)}.${raw(column.camelName)} && (ctrl.autocomplete.${raw(column.camelName)}.lookup[${raw(opts.camelName)}.${raw(column.camelName)}] || ctrl.autocomplete.${raw(column.camelName)}.load(${raw(opts.camelName)}.${raw(column.camelName)}))}}</td>`
     } else {
-      return html`<td md-cell>{{${raw(opts.camelName)}.${raw(column.camelName)}}}</td>`
+      return html`<td ng-hide="${hideCriteria(column)}" md-cell>{{${raw(opts.camelName)}.${raw(column.camelName)}}}</td>`
     }
   }
   const template = html`
@@ -28,7 +31,7 @@ const list = (opts) => {
                 <tr md-row md-select="${opts.camelName}" md-select-id="name" md-auto-select ng-repeat="${raw(opts.camelName)} in ctrl.data track by ${raw(opts.camelName)}.id">
                   ${opts.columns.filter(c => c.inList).map(c => c.cell || defaultCell(c))}
                   <td md-cell>
-                    <md-button ng-href="${opts.appPrefix}/{{${raw(opts.camelName)}.id}}">
+                    <md-button ng-href="{{::ctrl.appPrefix}}/{{${raw(opts.camelName)}.id}}">
                       <md-icon md-svg-icon="${editIcon}"></md-icon>
                       Edit
                     </md-button>
@@ -43,7 +46,7 @@ const list = (opts) => {
             </table>
         </md-table-container>
         <div layout="row" layout-align="end">
-          <md-button class="md-fab" aria-label="Add ${opts.titleName}" ng-href="${opts.appPrefix}/new">
+          <md-button class="md-fab" aria-label="Add ${opts.titleName}" ng-href="{{::ctrl.appPrefix}}/new">
             <md-icon md-svg-src="${createIcon}"></md-icon>
           </md-button>
         </div>
@@ -53,8 +56,13 @@ const list = (opts) => {
   app.component(`app${opts.pascalPlural}Page`, {
     template,
     controllerAs: 'ctrl',
-    controller: function(api, $mdToast) {
-      const crud = api.crud(opts.apiPrefix)
+    controller: function(api, $mdToast, $routeParams, util, $interpolate, $scope) {
+      this.$routeParams = $routeParams
+      this.apiPrefix = util.fillPath(opts.apiPrefix, $routeParams)
+      this.appPrefix = util.fillPath(opts.appPrefix, $routeParams)
+
+      const crud = api.crud(this.apiPrefix)
+      
       this.template = template
       this.selected = []
       this.data = []
@@ -82,7 +90,7 @@ const list = (opts) => {
       this.autocomplete = {}
 
       opts.columns.filter(c => c.type === 'autocomplete').forEach(c => {
-        const crud = api.crud(c.apiPrefix)
+        const crud = api.crud(util.fillPath(c.apiPrefix, $routeParams))
         let timer = null
         let ids = []
         const load = () => {
@@ -106,7 +114,7 @@ const list = (opts) => {
             return ac.lookup[id]
           }
         }
-      })     
+      })
     }
 
   })

+ 6 - 3
app/crud/pages/trash.js

@@ -37,8 +37,11 @@ const list = (opts) => {
       </app-user-area>
     `,
     controllerAs: 'ctrl',
-    controller: function(api, $mdToast, $location) {
-      const crud = api.crud(`${opts.apiPrefix}`)
+    controller: function(api, $mdToast, $location, util, $routeParams) {
+      this.apiPrefix = util.fillPath(opts.apiPrefix, $routeParams)
+      this.appPrefix = util.fillPath(opts.appPrefix, $routeParams)
+
+      const crud = api.crud(this.apiPrefix)
 
       this.selected = []
       this.data = []
@@ -53,7 +56,7 @@ const list = (opts) => {
         try {
           await crud.undelete(rec.id)
           $mdToast.showSimple(`${opts.titleName} undeleted.`)
-          $location.url(`${opts.appPrefix}`)
+          $location.url(this.appPrefix)
         } catch (err) {
           console.error(err)
           $mdToast.showSimple(`Could not undelete ${opts.titleName}: ${err.message || err}`)

+ 1 - 0
app/index.js

@@ -1,3 +1,4 @@
 require('./app')
 require('./components')
 require('./api-service')
+require('./util')

+ 5 - 0
app/util.js

@@ -0,0 +1,5 @@
+const app = require('./app')
+const util = require('../lib/util')
+app.service('util', () => util)
+module.exports = util
+

+ 3 - 1
auto-crud/app-factory.js

@@ -9,7 +9,9 @@ const appFactory = () => {
   cruds.forEach(crud => {
     pages(crud)
     app.config(($routeProvider) => {
-      $routeProvider.crudRoutes(crud)
+      if (crud.addRoutes !== false) {
+        $routeProvider.crudRoutes(crud)
+      }
    })
   })
 

+ 4 - 2
lib/crud/controller.js

@@ -44,7 +44,8 @@ const crudController = (opts) => {
     }
   }
   const create = async (req, res) => {
-    const data = (await Type.create(req.body))
+    const record = { ...req.body, ...(await subset(req))}
+    const data = (await Type.create(record))
     res.status(200).send(data && data.sanitize ? data.sanitize() : data)
   }
   const read = async (req, res) => {
@@ -52,7 +53,8 @@ const crudController = (opts) => {
     res.status(200).send(data && data.sanitize ? data.sanitize() : data)
   }
   const update = async (req, res) => {
-    const data = (await Type.update(req.body, { where: { id: req.params[opts.routeParam] } }))
+    const record = _.omit(req.body, _.keys(await subset(req)))
+    const data = (await Type.update(record, { where: { id: req.params[opts.routeParam] } }))
     res.status(200).send(data && data.sanitize ? data.sanitize() : data)
   }
   const $delete = async (req, res) => {

+ 6 - 0
lib/util.js

@@ -0,0 +1,6 @@
+const fillPath = (path, data) =>
+  path.replace(/:(\w+)/g, (text, key) => data[key] || text)
+
+module.exports = {
+  fillPath
+}