diff --git a/client/src/lists/fields/CUD.js b/client/src/lists/fields/CUD.js index 19918ae4..b47afd90 100644 --- a/client/src/lists/fields/CUD.js +++ b/client/src/lists/fields/CUD.js @@ -70,6 +70,7 @@ export default class CUD extends Component { if (data.default_value === null) { data.default_value = ''; } + // TODO: Construct form fields from settings }); } else { @@ -79,6 +80,7 @@ export default class CUD extends Component { key: '', default_value: '', group: null, + renderTemplate: '', orderListBefore: 'end', // possible values are / 'end' / 'none' orderSubscribeBefore: 'end', orderManageBefore: 'end', @@ -110,6 +112,12 @@ export default class CUD extends Component { } else { state.setIn(['key', 'error'], null); } + + // TODO: Validate field settings: + // TODO: parse and check options for enums + // TODO: make sure group is selected for option + // TODO: check default date/birthday is in the right format + // TODO: check number is a number } async submitHandler() { @@ -132,6 +140,8 @@ export default class CUD extends Component { if (data.default_value.trim() === '') { data.default_value = null; } + + // TODO: Construct settings field }); if (submitSuccessful) { @@ -159,17 +169,124 @@ export default class CUD extends Component { const t = this.props.t; const isEdit = !!this.props.entity; -/* - const orderColumns = [ - { data: 1, title: t('Field name'), sortable: false, searchable: false } - ]; -*/ - const typeOptions = Object.keys(this.fieldTypes).map(key => ({key, label:this.fieldTypes[key].label})); const type = this.getFormValue('type'); - // + let fieldSettings = null; + switch (type) { + case 'text': + case 'website': + case 'longtext': + case 'gpg': + case 'number': + fieldSettings = +
+ +
; + break; + + case 'checkbox': + case 'radio-grouped': + case 'dropdown-grouped': + fieldSettings = +
+ You can control the appearance of the merge tag with this template. The template + uses handlebars syntax and you can find all values from {'{{values}}'} array, for + example {'{{#each values}} {{this}} {{/each}}'}. If template is not defined then + multiple values are joined with commas.} + /> +
; + break; + + case 'radio-enum': + case 'dropdown-enum': + fieldSettings = +
+
Specify the options to select from in the following format:key|label. For example:
+
au|Australia
at|Austria
} + /> + Default key (e.g. au used when the field is empty.')}/> + You can control the appearance of the merge tag with this template. The template + uses handlebars syntax and you can find all values from {'{{values}}'} array. + Each entry in the array is an object with attributes key and label. + For example {'{{#each values}} {{this.value}} {{/each}}'}. If template is not defined then + multiple values are joined with commas.} + /> +
; + break; + + case 'date': + fieldSettings = +
+ + Default value used when the field is empty.')}/> +
; + break; + + case 'birthday': + fieldSettings = +
+ + Default value used when the field is empty.')}/> +
; + break; + + case 'json': + fieldSettings =
+ Default key (e.g. au used when the field is empty.')}/> + You can use this template to render JSON values (if the JSON is an array then the array is + exposed as values, otherwise you can access the JSON keys directly).} + /> +
; + break; + + case 'option': + const fieldsGroupedColumns = [ + { data: 4, title: "#" }, + { data: 1, title: t('Name') }, + { data: 2, title: t('Type'), render: data => this.fieldTypes[data].label, sortable: false, searchable: false }, + { data: 3, title: t('Merge Tag') } + ]; + + fieldSettings = +
+ + +
; + break; + } + return (
@@ -193,10 +310,8 @@ export default class CUD extends Component { - {/* type && this.fieldTypes[type].renderSettings */} -
- -
+ {fieldSettings} +
diff --git a/client/src/lists/fields/field-types.js b/client/src/lists/fields/field-types.js index eac9b0b9..f9e751ec 100644 --- a/client/src/lists/fields/field-types.js +++ b/client/src/lists/fields/field-types.js @@ -8,25 +8,21 @@ export function getFieldTypes(t) { const fieldTypes = { text: { label: t('Text'), - renderSettings: -
- -
}, website: { - label: t('Website') + label: t('Website'), }, longtext: { - label: t('Multi-line text') + label: t('Multi-line text'), }, gpg: { - label: t('GPG Public Key') + label: t('GPG Public Key'), }, number: { - label: t('Number') + label: t('Number'), }, checkbox: { - label: t('Checkboxes (from option fields)') + label: t('Checkboxes (from option fields)'), }, 'radio-grouped': { label: t('Radio Buttons (from option fields)') diff --git a/models/fields.js b/models/fields.js index c077e702..c565fa99 100644 --- a/models/fields.js +++ b/models/fields.js @@ -78,6 +78,7 @@ fieldTypes['date'] = fieldTypes['birthday'] = { grouped: false }; +const groupedTypes = Object.keys(fieldTypes).filter(key => fieldTypes[key].grouped); function hash(entity) { return hasher.hash(filterObject(entity, hashKeys)); @@ -150,6 +151,29 @@ async function listDTAjax(context, listId, params) { ); } +async function listGroupedDTAjax(context, listId, params) { + return await dtHelpers.ajaxListWithPermissions( + context, + [{ entityTypeId: 'list', requiredOperations: ['manageFields'] }], + params, + builder => builder + .from('custom_fields') + .innerJoin('lists', 'custom_fields.list', 'lists.id') + .where('custom_fields.list', listId) + .whereIn('custom_fields.type', groupedTypes), + [ 'custom_fields.id', 'custom_fields.name', 'custom_fields.type', 'custom_fields.key', 'custom_fields.order_list' ], + { + orderByBuilder: (builder, orderColumn, orderDir) => { + if (orderColumn === 'custom_fields.order_list') { + builder.orderBy(knex.raw('-custom_fields.order_list'), orderDir === 'asc' ? 'desc' : 'asc'); // This is MySQL speciality. It sorts the rows in ascending order with NULL values coming last + } else { + builder.orderBy(orderColumn, orderDir); + } + } + } + ); +} + async function serverValidate(context, listId, data) { const result = {}; @@ -342,6 +366,7 @@ module.exports = { getById, list, listDTAjax, + listGroupedDTAjax, create, updateWithConsistencyCheck, remove, diff --git a/routes/rest/fields.js b/routes/rest/fields.js index 79b0db01..f00863db 100644 --- a/routes/rest/fields.js +++ b/routes/rest/fields.js @@ -10,6 +10,10 @@ router.postAsync('/fields-table/:listId', passport.loggedIn, async (req, res) => return res.json(await fields.listDTAjax(req.context, req.params.listId, req.body)); }); +router.postAsync('/fields-grouped-table/:listId', passport.loggedIn, async (req, res) => { + return res.json(await fields.listGroupedDTAjax(req.context, req.params.listId, req.body)); +}); + router.getAsync('/fields/:listId/:fieldId', passport.loggedIn, async (req, res) => { const entity = await fields.getById(req.context, req.params.listId, req.params.fieldId); entity.hash = fields.hash(entity);