UNPKG

landmark-serve

Version:

Web Application Framework and Admin GUI / Content Management System built on Express.js and Mongoose

424 lines (388 loc) 19.1 kB
extends ../layout/base include ../mixins/rows include ../mixins/pagination block css if list.fieldTypes.markdown link(rel="stylesheet", href="/landmark/js/lib/bootstrap-markdown/css/bootstrap-markdown.css") block js if list.fieldTypes.location script(src="/landmark/js/common/ui-location.js") if list.fieldTypes.markdown script(src='/landmark/js/lib/marked/marked.js') script(src='/landmark/js/lib/bootstrap-markdown/js/bootstrap-markdown.js') script(src="/landmark/js/common/ui-markdown.js") if list.fieldTypes.wysiwyg script(src='/landmark/js/lib/tinymce/tinymce.min.js') script(src='/landmark/js/lib/tinymce/jquery.tinymce.min.js') script(src="/landmark/js/common/ui-wysiwyg.js") script(src='/landmark/js/views/list.js') script(src='/landmark/js/lib/browserified/querystring.js') script(src='/landmark/js/lib/browserified/queryfilter.js') script. Landmark.list = { path: "#{list.path}", label: "#{list.label}", singular: "#{list.singular}", plural: "#{list.plural}", cols: !{JSON.stringify(colPaths)}, perPage: !{ Number(list.perPage) || 50 } }; Landmark.wysiwyg = { options: !{JSON.stringify(wysiwygOptions)} }; Landmark.items = !{ JSON.stringify(items) }; Landmark.search = "#{search}"; Landmark.filters = !{ JSON.stringify(filters) }; Landmark.sort = "#{sort.by}"; Landmark.query = "#{query}"; Landmark.csrf_query = "!{csrf_query}"; block content //- //- Create Item //- ------------------------------ if !list.get('nocreate') .create-item if list.get('autocreate') .toolbar a(href='?new' + csrf_query).btn.btn-default.btn-create.btn-create-item span.ion-plus-round | Create #{list.singular} else form(method='post', action='/landmark/' + list.path) input(type='hidden', name='action', value='create') input(type='hidden', name=csrf_token_key, value=csrf_token_value) .form h4 New #{list.singular} if list.nameIsInitial if list.nameField.type == 'name' .field.item-name: .col-sm-12: input(type='text', name=list.nameField.paths.full, value=submitted[list.nameField.paths.full], placeholder=list.singular + ' ' + list.nameField.label.toLowerCase()).form-control.input-lg else if list.nameField.type == 'text' .field.item-name: .col-sm-12: input(type='text', name=list.nameField.path, value=submitted[list.nameField.path], placeholder=list.singular + ' ' + list.nameField.label.toLowerCase()).form-control.input-lg else .alert.alert-danger Unsupported field type (#{list.nameField.type}) for item name (#{list.nameField.path}). each field in list.initialFields |!{field.render('initial',submitted) } .toolbar .toolbar-create button(type='submit').btn.btn-default.btn-create Create a(href=js).btn.btn-link.btn-cancel.btn-cancel-create-item cancel .toolbar-default a(href=js, class=(showCreateForm ? 'autoclick' : null)).btn.btn-default.btn-create.btn-create-item span.ion-plus-round | Create #{list.singular} hr //- //- List //- ------------------------------ - var xFilters = _.size(filters); .page-header.list-header(class=!items.total && !search && !xFilters ? 'empty-list' : '') h1 if items.total span.items-total= utils.plural(items.total, '* ' + list.singular, '* ' + list.plural) else span.items-total No #{list.plural.toLowerCase()} found = search || xFilters ? '' : '.' .search-sort(style='display:inline-block') if search span.text-muted matching span=search if xFilters span.text-muted and span= utils.plural(xFilters, ' * other filter') else if xFilters span.text-muted matching span= utils.plural(xFilters, ' * filter') //- Sort if items.results.length if sort.by span.text-muted ordered by  else span.text-muted — span.dropdown.list-sort-dropdown a(href=js, data-toggle='dropdown').dropdown-toggle if sort.label = sort.label.toLowerCase() + ' ' if sort.inv | (descending) else | sort by b.caret ul.dropdown-menu if list.get('sortable') if sort.by == 'sortOrder' li: a(href=link_to({sort: '-sortOrder'})) span(class='fieldicon fieldicon-sort') span Display Order (inverted) else if sort.by == '-sortOrder' li: a(href=link_to({sort: 'sortOrder'})) span(class='fieldicon fieldicon-sort') span Display Order (normal) else li: a(href=link_to({sort: 'sortOrder'})) span(class='fieldicon fieldicon-sort') span Display Order each el in list.uiElements if el.type == 'heading' li.dropdown-header= el.heading else if el.type == 'field' if el.field.type != 'relationship' && !el.field.nosort && !el.field.hidden if sort.field && sort.field.path == el.field.path if sort.inv li: a(href=link_to({ sort: el.field.path })) span(class='fieldicon fieldicon-' + el.field.type) span= el.field.label + ' (ascending)' else li: a(href=link_to({ sort: '-' + el.field.path })) span(class='fieldicon fieldicon-' + el.field.type) span= el.field.label + ' (descending)' else li: a(href=link_to({ sort: el.field.path })) span(class='fieldicon fieldicon-' + el.field.type) span= el.field.label //- //- Filters //- ------------------------------ form#list-filters //- //- Search Bar //- ------------------------------ if items.results.length || search || xFilters .list-toolbar .search-list .form-inline .dropdown-inline.list-filter-dropdown.js-recent-searches.hidden.mr-5 a(href=js, data-toggle='dropdown', title='Recent Filters').btn.btn-default.dropdown-toggle span.sr-only Recent Filters span.ion-clock | span.caret ul.dropdown-menu li.dropdown-header Recent Filters .form-inline.searchbox-form.mr-5 .searchbox-field input(type='search', name='search', placeholder='Search #{list.plural.toLowerCase()}', value=search).form-control.js-search-list.searchbox-input if search a(href='/landmark/#{list.path}', title='Clear search').search-list-clear × .searchbox-button: button(type='submit').btn.btn-default.btn-search.searchbox-submit span.visible-xs Go span.hidden-xs Search //- .search-field input(type='text', name='search', placeholder='Search ' + list.plural.toLowerCase(), value=search).form-control //- span.search-button: button(type='submit').btn.btn-default.btn-search span.visible-xs Go span.hidden-xs Search .dropdown-inline.list-filter-dropdown a(href=js, data-toggle='dropdown').btn.btn-default.dropdown-toggle | Add Filter b.caret ul.dropdown-menu: each el in list.uiElements if el.type == 'heading' li.dropdown-header= el.heading else if el.type == 'field' && !el.field.nofilter && !el.field.hidden li: a(href=js, data-path=el.field.path).add-list-filter span(class='fieldicon fieldicon-' + el.field.type) span= el.field.label //- lg screens only //- columns and download aren't relevant when using a mobile device if items.total .pull-right.hidden-xs .pull-right a(href=download_link, data-confirm="Download a .csv of #{utils.plural(items.total, '* ' + list.singular, '* ' + list.plural).toLowerCase()}?").btn.btn-default Download div(style='margin-right: 10px;').dropdown.list-columns-dropdown.pull-right a(href=js, data-toggle='dropdown').btn.btn-default.dropdown-toggle | Columns b.caret ul.dropdown-menu each el in list.uiElements if el.type == 'heading' li.dropdown-header= el.heading else if el.type == 'field' && !el.field.nocol && !el.field.hidden li: a(href=js, data-col=el.field.path).btn-toggle-column span(class=_.contains(colPaths, el.field.path) ? 'ion-checkmark' : '').icon span= el.field.label if query.cols li.divider li: a(href=link_to({ cols: undefined })) span.ion-close.icon span Reset to default //- //- List Filters //- ------------------------------ .list-filters each field in list.fields if !field.nofilter && !field.hidden .filter(data-path=field.path, data-type=field.type, class=filters[field.path] ? 'active' : '') .filter-label a(href=js, title='Remove filter', data-toggle='tooltip', data-placement='right', data-container='body').btn-companion-xs.clear-filter × span(class='fieldicon fieldicon-' + field.type) .btn-companion-xs=field.label .filter-options - var ops = filters[field.path] || {} //- Text Fields if field.type == 'text' || field.type == 'textarea' || field.type == 'html' || field.type == 'email' || field.type == 'url' || field.type == 'key' .btn-group.btn-group-xs(data-toggle='buttons') button(type='button', class=!ops.exact ? 'active' : '').btn.btn-default input(type='radio', name='options') | contains button(type='button', class=ops.exact ? 'active' : '', data-opt='exact', data-value='true').btn.btn-default input(type='radio', name='options') | exact match .filter-input.filter-input-md: input(type='text', name='value', value=ops.value).form-control.input-xs button(type='button', data-toggle='button', class=ops.inv ? 'active' : '', data-opt='inv', data-value='true').btn.btn-xs.btn-default invert //- Numeric Fields if field.type == 'number' || field.type == 'money' .btn-group.btn-group-operator.btn-group-xs(data-toggle='buttons') button(type='button', class=!ops.operator ? 'active' : '').btn.btn-default input(type='radio', name='options') | exactly button(type='button', class=ops.operator == 'gt' ? 'active' : '', data-opt='operator', data-value='gt').btn.btn-default input(type='radio', name='options') | greater than button(type='button', class=ops.operator == 'lt' ? 'active' : '', data-opt='operator', data-value='lt').btn.btn-default input(type='radio', name='options') | less than button(type='button', class=ops.operator == 'bt' ? 'active' : '', data-opt='operator', data-value='bt').btn.btn-default input(type='radio', name='options') | between .filter-input .filter-input-range.form-inline.text-sm(style='display:none') input(type='number', value=(ops.operator === 'bt' && ops.value[0])).form-control.input-xs.field-inline--xs.filter-input-range1 span.mh-1 and input(type='number', value=(ops.operator === 'bt' && ops.value[1])).form-control.input-xs.field-inline--xs.filter-input-range2 span.mh-1 (inclusive) .filter-input-standard(style='display:none') input(type='number', name='value', value=(ops.operator !== 'bt' && ops.value)).form-control.input-xs //- Date if field.type == 'date' || field.type == 'datetime' .btn-group.btn-group-operator.btn-group-xs(data-toggle='buttons') button(type='button', class=(!ops.operator) ? 'active' : '').btn.btn-default input(type='radio', name='options') | on button(type='button', class=ops.operator == 'gt' ? 'active' : '', data-opt='operator', data-value='gt').btn.btn-default input(type='radio', name='options') | after button(type='button', class=ops.operator == 'lt' ? 'active' : '', data-opt='operator', data-value='lt').btn.btn-default input(type='radio', name='options') | before button(type='button', class=ops.operator == 'bt' ? 'active' : '', data-opt='operator', data-value='bt').btn.btn-default input(type='radio', name='options') | between .filter-input .filter-input-range.form-inline.text-sm(style='display:none') input(type='text', value=(ops.operator === 'bt' && ops.value[0])).form-control.input-xs.field-inline--sm.ui-datepicker.filter-input-range1 span.mh-1 and input(type='text', value=(ops.operator === 'bt' && ops.value[1])).form-control.input-xs.field-inline--sm.ui-datepicker.filter-input-range2 span.mh-1 (inclusive) .filter-input-standard(style='display:none') input(type='text', name='value', value=(ops.operator !== 'bt' && ops.value)).form-control.input-xs.ui-datepicker //- Location if field.type == 'location' - ops.value = ops.value || [] .filter-input.filter-input-md: input(type='text', name='address', value=ops.value[0], placeholder='Address').form-control.input-xs .filter-input.filter-input-sm: input(type='text', name='suburb', value=ops.value[1], placeholder='Suburb').form-control.input-xs .filter-input.filter-input-sm: input(type='text', name='state', value=ops.value[2], placeholder='State').form-control.input-xs .filter-input.filter-input-sm: input(type='text', name='postcode', value=ops.value[3], placeholder='Postcode').form-control.input-xs .filter-input.filter-input-sm: input(type='text', name='country', value=ops.value[4], placeholder='Country').form-control.input-xs //- Select if field.type == 'select' .btn-group.btn-group-xs(data-toggle='buttons') button(type='button', class=!ops.inv ? 'active' : '').btn.btn-default input(type='radio', name='options') | is button(type='button', class=ops.inv ? 'active' : '', data-opt='inv', data-value='true').btn.btn-default input(type='radio', name='options') | is not .filter-input.filter-input-md: select(name='value') option(value='') each op in field.ops option(value=op.value, selected=ops.value == op.value)= op.label //- Boolean if field.type == 'boolean' .btn-group.btn-group-xs(data-toggle='buttons') button(type='button', class=ops.value !== false ? 'active' : '', data-opt='value', data-value='true').btn.btn-default input(type="radio", name="options") | checked button(type='button', class=ops.value === false ? 'active' : '', data-opt='value', data-value='false').btn.btn-default input(type="radio", name="options") | not checked //- Cloudinary Image if field.type == 'cloudinaryimage' .btn-group.btn-group-xs(data-toggle='buttons') button(type='button', class=ops.value !== false ? 'active' : '', data-opt='value', data-value='true').btn.btn-default input(type="radio", name="options") | has image button(type='button', class=ops.value === false ? 'active' : '', data-opt='value', data-value='false').btn.btn-default input(type="radio", name="options") | has no image //- S3 File if field.type == 's3file' .btn-group.btn-group-xs(data-toggle='buttons') button(type='button', class=ops.value !== false ? 'active' : '', data-opt='value', data-value='true').btn.btn-default input(type="radio", name="options") | has file button(type='button', class=ops.value === false ? 'active' : '', data-opt='value', data-value='false').btn.btn-default input(type="radio", name="options") | has no file //- Local File if field.type == 'localfile' .btn-group.btn-group-xs(data-toggle='buttons') button(type='button', class=ops.value !== false ? 'active' : '', data-opt='value', data-value='true').btn.btn-default input(type="radio", name="options") | has file button(type='button', class=ops.value === false ? 'active' : '', data-opt='value', data-value='false').btn.btn-default input(type="radio", name="options") | has no file //- Relationships if field.type == 'relationship' .btn-group.btn-group-xs(data-toggle='buttons') button(type='button', class=ops.inv ? '' : 'active', data-opt='inv', data-value='false').btn.btn-default input(type="radio", name="options") | linked to button(type='button', class=ops.inv ? 'active' : '', data-opt='inv', data-value='true').btn.btn-default input(type="radio", name="options") | not linked to input(type='hidden', name=field.path, id='field_' + field.path, value=ops.value, data-ref-path=field.refList.path, data-ref-singular=field.refList.singular, data-ref-plural=field.refList.plural).ui-select2-ref .list-filters-action button(type='submit').btn.btn-default.btn-filter Search a(href='/landmark/#{list.path}').btn.btn-link.btn-cancel clear filters if items.results.length //- //- Pagination //- ------------------------------ .list-pagination if items.totalPages > 1 +pagination(items) else .count Showing #{utils.plural(items.total, '* ' + list.singular, '* ' + list.plural)} - var sortable = list.get('sortable') && !list.get('sortContext') && sort.by == 'sortOrder' - var firstColspan = 1; .items-list-wrapper: table(cellpadding=0, cellspacing=0, class=sortable ? 'sortable' : false, data-list-path=list.path).table.items-list if !list.get('nodelete') - firstColspan++; col(width=26) if sortable - firstColspan++; col(width=26) each col in columns col(width=col.width) thead tr each col, i in columns th(colspan=i == 0 && firstColspan > 1 ? firstColspan : false) if col.field && sort.field && sort.field.path == col.field.path if sort.inv a(href=link_to({ sort: col.field.path }), title='Sort by ' + col.label + ' (asc)', class='th-sort--desc').th-sort = col.label span.th-sort__icon else a(href=link_to({ sort: '-' + col.field.path }), title='Sort by ' + col.label + ' (desc)', class='th-sort--asc').th-sort = col.label span.th-sort__icon else if col.field a(href=link_to({ sort: col.field.path }), title='Sort by ' + col.label).th-sort= col.label span.th-sort__icon else = col.label tbody each item in items.results +row(list, colums, item) if items.totalPages > 1 .list-pagination +pagination(items)