Created
April 11, 2019 13:56
-
-
Save branquito/8b742ab34a589d9c08f2436e0330416f to your computer and use it in GitHub Desktop.
datatables
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <template> | |
| <div> | |
| <div | |
| v-if="loadedFilters && Object.keys(loadedFilters).length > 0 && filtersType" | |
| class="active-filters"> | |
| <h4 class="applied-filters-text">{{ $tc('filters.applied_filters') }}:</h4> | |
| <div | |
| v-for="(loadedFilter, key) in loadedFilters" | |
| :key="key" | |
| class="filter-btn-group btn-group mr-2" | |
| role="group"> | |
| <rn-button | |
| color="default" | |
| size="xs"> | |
| {{ loadedFilter.label }}: <b>{{ loadedFilter.value }}</b></rn-button | |
| > | |
| <rn-button | |
| v-tooltip.top="{ content: $tc('filters.remove_filter') }" | |
| :should-confirm="true" | |
| color="danger" | |
| size="xs" | |
| icon="fa-times" | |
| @click="removeFilters(loadedFilter)" | |
| /> | |
| </div> | |
| </div> | |
| <template v-if="filtersType"> | |
| <transition name="slide-fade"> | |
| <rn-filters-drawer | |
| v-show="filtersDrawerToggle" | |
| ref="filterDrawer" | |
| :title="filtersTitle || fileName + ' Filters'" | |
| :type="filtersType" | |
| :saved-filter-buttons="savedFilterButtons" | |
| :constant-filters="constantFilters" | |
| :hide-filters="hideFilters" | |
| class="drawer" | |
| @close="filtersDrawerToggle = false" | |
| @filter="changeFilters" | |
| /> | |
| </transition> | |
| </template> | |
| <table | |
| :id="id" | |
| width="100%" | |
| class="table table-bordered"> | |
| <thead> | |
| <tr> | |
| <slot /> | |
| </tr> | |
| </thead> | |
| <tfoot v-if="totalsInFooter"> | |
| <tr> | |
| <th | |
| v-for="column in columns" | |
| :key="column.name" /> | |
| </tr> | |
| </tfoot> | |
| <tbody /> | |
| </table> | |
| </div> | |
| </template> | |
| <script> | |
| import $ from 'jquery' | |
| import _ from 'lodash' | |
| import axios from 'axios' | |
| import RnButton from '@components/ui/buttons/Button' | |
| import rnFiltersDrawer from '@components/ui/filters/FiltersDrawer' | |
| import { registeredEvents } from './config' | |
| window.JSZip = require('jszip') | |
| require('imports-loader?define=>false!datatables.net')(null, $) | |
| require('imports-loader?define=>false!datatables.net-bs')(null, $) | |
| require('imports-loader?define=>false!datatables.net-buttons')(null, $) | |
| require('imports-loader?define=>false!datatables.net-buttons-bs')(null, $) | |
| // require('imports-loader?define=>false!pdfmake/build/pdfmake.js') | |
| // require('imports-loader?this=>window!pdfmake/build/vfs_fonts.js') | |
| require('imports-loader?define=>false!datatables.net-responsive')(null, $) | |
| require('imports-loader?define=>false!datatables.net-select')(null, $) | |
| require('imports-loader?define=>false!datatables.net-responsive-bs')(null, $) | |
| require('imports-loader?define=>false!datatables.net-colreorder')(null, $) | |
| require('imports-loader?define=>false!datatables.net-buttons/js/buttons.print.js')(null, $) | |
| require('imports-loader?define=>false!datatables.net-buttons/js/buttons.html5.js')(null, $) | |
| require('imports-loader?define=>false!datatables.net-buttons/js/buttons.colVis.js')(null, $) | |
| export default { | |
| components: { | |
| RnButton, | |
| rnFiltersDrawer | |
| }, | |
| props: { | |
| handlers: { | |
| type: Object, | |
| default: () => {}, | |
| validator: value => { | |
| return Object(value) === value && Object.keys(value).every(item => typeof value[item] === 'function') | |
| } | |
| }, | |
| savedFilterButtons: { | |
| type: Boolean, | |
| default: true | |
| }, | |
| dataTableOptions: { | |
| type: Object, | |
| default: null | |
| }, | |
| refreshButton: { | |
| type: Boolean, | |
| default: null | |
| }, | |
| showColVis: { | |
| type: Boolean, | |
| default: null | |
| }, | |
| additionalButtons: { | |
| type: String, | |
| default: null | |
| }, | |
| showExportButtons: { | |
| type: Boolean, | |
| default: null | |
| }, | |
| columns: { | |
| type: Array, | |
| default: null | |
| }, | |
| filters: { | |
| type: Object, | |
| default: null | |
| }, | |
| hideFilters: { | |
| type: Array, | |
| default: null | |
| }, | |
| fileName: { | |
| type: String, | |
| default: null | |
| }, | |
| url: { | |
| type: String, | |
| default: null | |
| }, | |
| customButtons: { | |
| type: Array, | |
| default: null | |
| }, | |
| batchSelect: { | |
| type: String, | |
| default: '' | |
| }, | |
| totalsInFooter: { | |
| type: Array, | |
| default: null | |
| }, | |
| filtersType: { | |
| type: String, | |
| default: null | |
| }, | |
| filtersTitle: { | |
| type: String, | |
| required: false, | |
| default: null | |
| }, | |
| constantFilters: { | |
| type: Object, | |
| default: null | |
| }, | |
| method: { | |
| type: String, | |
| default: 'POST' | |
| }, | |
| tableId: { | |
| type: String, | |
| default: 'none' | |
| }, | |
| employeePortal: { | |
| type: Boolean, | |
| default: false | |
| } | |
| }, | |
| data () { | |
| return { | |
| dt: null, | |
| totals: null, | |
| filtersDrawerToggle: false, | |
| loadedFilters: null, | |
| id: this.randomId() | |
| } | |
| }, | |
| computed: { | |
| dtStateUrl () { | |
| return this.employeePortal ? '/ep/datatable-settings' : '/datatable-settings' | |
| }, | |
| options () { | |
| const options = { | |
| processing: true, | |
| serverSide: true, | |
| select: !!this.batchSelect, | |
| columns: this.columns, | |
| buttons: this.buttons, | |
| deferRender: true, | |
| pageLength: 10, | |
| responsive: { | |
| details: { | |
| renderer: (api, rowIdx, columns) => { | |
| const data = $.map(columns, (col, i) => { | |
| return col.hidden | |
| ? `<tr data-dt-row="${col.rowIndex}" data-dt-column="${col.columnIndex}"> | |
| <td>${col.title}:</td> | |
| <td>${col.data}</td> | |
| </tr>` | |
| : '' | |
| }).join('') | |
| return data ? $('<table/>').append(data) : false | |
| } | |
| } | |
| }, | |
| pagingType: 'full_numbers', | |
| autoWidth: false, | |
| stateSave: true, | |
| stateSaveCallback: (settings, data) => { | |
| const hash = `${this.tableId}_${window.location.hash}` | |
| axios.post(this.dtStateUrl, { | |
| hash, | |
| payload: data | |
| }) | |
| }, | |
| stateLoadCallback: (settings, callback) => { | |
| const hash = `${this.tableId}_${window.location.hash}` | |
| axios.get(this.dtStateUrl, { | |
| params: { | |
| filter: {hash: hash} | |
| } | |
| }).then(response => { | |
| callback(response.data.data[response.data.data.length - 1].payload) | |
| }).catch(error => { | |
| callback({}) | |
| }) | |
| }, | |
| stateDuration: 0, | |
| colReorder: true, | |
| ajax: { | |
| method: this.method, | |
| contentType: 'application/json', | |
| headers: { | |
| 'X-CSRF-Token': $('[name=csrf-token]').attr('content') | |
| }, | |
| url: this.url, | |
| dataType: this.method === 'GET' ? '' : 'json', | |
| data: data => { | |
| data.filter = this.filters | |
| if (this.totalsInFooter) { | |
| data.totals = this.totalsInFooter | |
| } | |
| return this.method === 'GET' ? data : JSON.stringify(data) | |
| } | |
| }, | |
| lengthMenu: [5, 10, 25, 50, 75, 100], | |
| dom: this.dom, | |
| renderer: 'bootstrap', | |
| language: { | |
| lengthMenu: '_MENU_ entries per page', | |
| processing: '<i class="fa fa-refresh fa-spin"></i> Processing Data...', | |
| paginate: { | |
| next: '<i class="fa fa-chevron-right"></i>', | |
| previous: '<i class="fa fa-chevron-left"></i>' | |
| }, | |
| select: { | |
| rows: { | |
| _: ' (Selected %d rows)', | |
| 1: ' (Selected 1 row)' | |
| } | |
| } | |
| }, | |
| drawCallback (settings) { | |
| ;['success', 'warning', 'danger'].forEach(color => { | |
| this.$(`.dt-cell-color-${color}`).each((i, el) => { | |
| $(el) | |
| .parent() | |
| .addClass(color) | |
| }) | |
| }) | |
| $('[data-toggle="popover"]').popover({ | |
| html: true, | |
| trigger: 'hover', | |
| container: 'body' | |
| }) | |
| } | |
| // columnDefs: [{ | |
| // // targets: [3, 4, 7, 9, 11, 13, 15, 18], | |
| // createdCell: function (td, data, rowData, row, col) { | |
| // // Coloring for projects | |
| // let dateCell = $(td).find('.dt-date-exceeded') | |
| // $(td).each(function () { | |
| // if (dateCell.length > 0) { | |
| // $(this).addClass('danger') | |
| // } | |
| // }) | |
| // } | |
| // }] | |
| } | |
| if (this.totalsInFooter) { | |
| const vm = this | |
| options.footerCallback = function (row, data, start, end, display) { | |
| if (vm.totals) { | |
| for (let column in vm.totals) { | |
| let footer = vm.dt.column(`${column}:name`).footer() | |
| $(footer).html(vm.totals[column]) | |
| } | |
| } | |
| } | |
| } | |
| return options | |
| }, | |
| dom () { | |
| var additionalButtons = '' | |
| if (this.additionalButtons) { | |
| additionalButtons = '<"pull-right additional-buttons">' | |
| } | |
| var searchBox = '' | |
| if (this.searching === true) { | |
| searchBox = 'f' | |
| } | |
| return `<"row" | |
| <"col-xs-12 col-sm-6 col-left"l> | |
| <"col-xs-12 col-sm-6 col-right" | |
| <"pull-right"B${searchBox}>${additionalButtons}> | |
| > | |
| <"row" | |
| <"col-sm-12"tr> | |
| > | |
| <"row" | |
| <"col-xs-12 col-sm-6 col-left"p> | |
| <"col-xs-12 col-sm-6 col-right"i> | |
| >` | |
| }, | |
| buttons () { | |
| const buttonTypes = [] | |
| const exportButton = { | |
| title: this.fileName || 'export', | |
| exportOptions: { | |
| columns: function (idx, data, node) { | |
| if (!$(node).is(':visible')) { | |
| return false | |
| } | |
| const printable = $(node).data('printable') | |
| if (printable) { | |
| return printable === 'yes' | |
| } | |
| return true | |
| }, | |
| format: { | |
| body: function (data, column, row) { | |
| var preparedData = '<div>' + data + '</div>' | |
| return $(preparedData) | |
| .html() | |
| .indexOf('<select') === 0 | |
| ? $(data) | |
| .children('option:selected') | |
| .text() | |
| : $(preparedData).text() | |
| } | |
| } | |
| } | |
| } | |
| if (this.customButtons) { | |
| buttonTypes.push(this.customButtons) | |
| } | |
| if (this.batchSelect) { | |
| buttonTypes.push( | |
| { | |
| extend: 'selectAll', | |
| text: '<i title="Select All" data-toggle="tooltip" class="fa fa-check-square"></i>' | |
| }, | |
| { | |
| extend: 'selectNone', | |
| text: '<i title="Deselect All" data-toggle="tooltip" class="fa fa-square"></i>' | |
| } | |
| ) | |
| } | |
| if (this.showExportButtons) { | |
| buttonTypes.push( | |
| { | |
| text: '<i title="Reload Data" data-toggle="tooltip" class="fa fa-refresh"></i>', | |
| action: function (e, dt) { | |
| dt.ajax.reload() | |
| }, | |
| className: 'reload' | |
| }, | |
| _.assign({}, exportButton, { | |
| extend: 'print', | |
| text: '<i title="Print Table" data-toggle="tooltip" class="fa fa-print"></i>', | |
| title: this.fileName | |
| }), | |
| _.assign({}, exportButton, { | |
| extend: 'excelHtml5', | |
| text: '<i title="Export To Excel" data-toggle="tooltip" class="fa fa-file-excel-o"></i>', | |
| title: this.fileName | |
| }), | |
| _.assign({}, exportButton, { | |
| extend: 'csvHtml5', | |
| text: '<i title="Export To CSV" data-toggle="tooltip" class="fa fa-file-o"></i>', | |
| title: this.fileName | |
| }) | |
| // _.assign({}, exportButton, { | |
| // extend: 'pdf', | |
| // text: '<i title="Export To PDF" data-toggle="tooltip" class="fa fa-file-pdf-o"></i>', | |
| // orientation: 'landscape', | |
| // pageSize: 'A3' | |
| // }), | |
| ) | |
| } | |
| if (this.filtersType) { | |
| buttonTypes.push( | |
| _.assign({}, exportButton, { | |
| text: '<i data-toggle="tooltip" class="fa fa-filter"></i> Filters', | |
| action: () => { | |
| this.filtersDrawerToggle = true | |
| } | |
| }) | |
| ) | |
| } | |
| if (this.showColVis) { | |
| buttonTypes.push( | |
| _.assign({}, exportButton, { | |
| extend: 'colvis', | |
| text: '<i title="Show/Hide Columns" data-toggle="tooltip" class="fa fa-cog"></i>', | |
| postfixButtons: ['colvisRestore'], | |
| columns: (a, b, column) => { | |
| return !$(column).hasClass('none') | |
| } | |
| }) | |
| ) | |
| } | |
| for (var i = 0; i < buttonTypes.length; i++) { | |
| if (!this.refreshButton && buttonTypes[i].className === 'reload') { | |
| buttonTypes.splice(i, 1) | |
| } | |
| } | |
| return buttonTypes | |
| } | |
| }, | |
| watch: { | |
| filtersDrawerToggle (value) { | |
| document.getElementsByTagName('body')[0].style.overflow = value ? 'hidden' : 'auto' | |
| } | |
| }, | |
| destroyed () { | |
| this.dt.destroy() | |
| }, | |
| mounted () { | |
| let el = document.getElementById(this.id) | |
| this.dt = $(el).DataTable(this.options) | |
| this.dt.on('page.dt', function () { | |
| $(':focus').blur() | |
| $('html, body').animate({ | |
| scrollTop: $(el).offset().top - 250 | |
| }, 400) | |
| }) | |
| const vm = this | |
| this.dt.on('responsive-display', (e, datatable, row, showHide, update) => { | |
| if (showHide) { | |
| $('.disapproval-notes').popover({ | |
| trigger: 'hover' | |
| }) | |
| } | |
| }) | |
| this.dt.on('draw', () => { | |
| $('[data-toggle=tooltip-manual]', el).each((i, el) => { | |
| $(el).tooltip({ | |
| container: $(el).parent() | |
| }) | |
| }) | |
| }) | |
| $(el) | |
| .on('click', registeredEvents.join(', '), function (e) { | |
| let actionGetter = vm.$_utils.getEventFromClassList( | |
| e.target.classList, | |
| ['action', 'btn'] // classes to exclude | |
| ) | |
| let resolvedAction = actionGetter().pop() | |
| if (vm.handlers) { | |
| // this check will become obsolete once all datatables instnces implement this interface | |
| if (!vm.$_utils.isCallable(resolvedAction, vm.handlers)) { | |
| console.warn('Handler not defined for this type of action') | |
| return | |
| } | |
| let data = $(this).data() | |
| vm.handlers[resolvedAction](data) | |
| } | |
| }) | |
| .on('click', '.vuejs', function (event) { | |
| event.preventDefault() | |
| event.stopPropagation() | |
| const url = $(this).attr('href') | |
| // vm.$router.push(url.replace(/https?:\/\/(.*)\.renhead\.(com|test)\/(.*)/, '$3')) | |
| vm.$router.push(url.split('#')[1]) | |
| }) | |
| .on('xhr.dt', (e, settings, json, xhr) => { | |
| if (vm.totalsInFooter) { | |
| vm.totals = json.totals | |
| } | |
| }) | |
| .on('click', '.timesheet-actions', function () { | |
| const event = $(this).data('action') | |
| vm.$emit(`${event}TimesheetEntry`, $(this).data()) | |
| }) | |
| .on('click', '.edit-timesheet', function () { | |
| // Approve expense | |
| vm.$emit(`editTimesheet`, $(this).data()) | |
| }) | |
| .on('click', '.delete-task-list', function () { | |
| // Delete onboarding task list | |
| vm.$emit(`deleteList`, $(this).data()) | |
| }) | |
| .on('click', '.expense-edit', function () { | |
| // Edit expense | |
| vm.$emit(`editExpense`, $(this).data()) | |
| }) | |
| .on('click', '.expense-remove', function () { | |
| // Remove expense | |
| vm.$emit(`deleteExpense`, $(this).data()) | |
| }) | |
| .on('click', '.approve-expense', function () { | |
| // Approve expense | |
| vm.$emit(`approveExpense`, $(this).data()) | |
| }) | |
| .on('click', '.disapprove-expense', function () { | |
| // Disapprove expense | |
| vm.$emit(`disapproveExpense`, $(this).data()) | |
| }) | |
| .on('click', '.final-approve-expense-entry', function () { | |
| // Approve expense | |
| vm.$emit(`finalApproveExpense`, $(this).data()) | |
| }) | |
| .on('click', '.final-disapprove-expense-entry', function () { | |
| // Disapprove expense | |
| vm.$emit(`finalDisapproveExpense`, $(this).data()) | |
| }) | |
| .on('click', '.edit-user-commission', function () { | |
| // Edit user commission | |
| vm.$emit(`editCommission`, $(this).data()) | |
| }) | |
| .on('click', '.edit-user', function () { | |
| // Edit user | |
| vm.$router.push({ path: `/settings/users/${$(this).data().id}` }) | |
| }) | |
| /* Timesheets & Expenses */ | |
| // Approve | |
| .on('click', '.approve-timesheet-entry, .approve-expense-entry', function () { | |
| vm.$emit(`approveItemEntry`, $(this).data()) | |
| }) | |
| // Disapprove | |
| .on('click', '.disapprove-timesheet-entry, .disapprove-expense-entry', function (e) { | |
| vm.$emit(`disapproveItemEntry`, $(this).data()) | |
| }) | |
| .on('click', '.final-approve-timesheet-entry, .final-approve-expense-entry', function () { | |
| vm.$emit(`finalApproveItemEntry`, $(this).data()) | |
| }) | |
| // Disapprove | |
| .on('click', '.final-disapprove-timesheet-entry, .final-disapprove-expense-entry', function (e) { | |
| vm.$emit(`finalDisapproveItemEntry`, $(this).data()) | |
| }) | |
| /* Pto Requests */ | |
| .on('click', '.manage-pto', function () { | |
| vm.$emit(`managePto`, $(this).data()) | |
| }) | |
| .on('click', '.approve-pto', function () { | |
| vm.$emit(`approvePto`, $(this).data()) | |
| }) | |
| .on('click', '.disapprove-pto', function () { | |
| vm.$emit(`disapprovePto`, $(this).data()) | |
| }) | |
| // EP Timesheets | |
| .on('click', '.delete-entry', function () { | |
| vm.$emit(`deleteTimesheetEntry`, $(this).data()) | |
| }) | |
| // Projects | |
| .on('click', '.delete-project', function () { | |
| vm.$emit('deleteProject', $(this).data()) | |
| }) | |
| .on('click', '.edit-limit-project', function () { | |
| vm.$emit('edit-project-limit', $(this).data()) | |
| }) | |
| .on('click', '.remove-project-jobreq', function () { | |
| vm.$emit('remove-jobreq-project', $(this).data()) | |
| }) | |
| .on('click', '.edit-bank-of-hours', function () { | |
| vm.$emit('edit', $(this).data()) | |
| }) | |
| .on('click', '.remove-bank-of-hours', function () { | |
| vm.$emit('delete', $(this).data()) | |
| }) | |
| // Statement of work | |
| .on('click', '.delete-sow', function () { | |
| vm.$emit('delete', $(this).data()) | |
| }) | |
| .on('click', '.edit-schedule-rate', function () { | |
| vm.$emit('edit', $(this).data()) | |
| }) | |
| .on('click', '.delete-schedule-rate', function () { | |
| vm.$emit('delete', $(this).data()) | |
| }) | |
| .on('click', '.edit-sow-payment-term', function () { | |
| vm.$emit('edit', $(this).data()) | |
| }) | |
| .on('click', '.delete-sow-payment-term', function () { | |
| vm.$emit('delete', $(this).data()) | |
| }) | |
| .on('click', '.edit-limit-sow', function () { | |
| vm.$emit('edit', $(this).data()) | |
| }) | |
| .on('click', '.remove-project-sow', function () { | |
| vm.$emit('delete', $(this).data()) | |
| }) | |
| .on('click', '.schedule-rate-cbx-done', function () { | |
| let done = $(this).data('done') | |
| $(this).data('done', !done) | |
| vm.$emit('mark-as-done', $(this).data()) | |
| }) | |
| // Scopes table on SOW | |
| .on('click', '.edit-scope-of-work', function () { | |
| vm.$emit('editScopeOfWork', $(this).data()) | |
| }) | |
| .on('click', '.delete-scope-of-work', function () { | |
| vm.$emit('deleteScopeOfWork', $(this).data()) | |
| }) | |
| $('.dataTables_length select').select2({ | |
| minimumResultsForSearch: -1 | |
| }) | |
| }, | |
| methods: { | |
| removeFilters (filter) { | |
| if (filter.removeExtended) { | |
| this.$refs.filterDrawer.removeExtended(filter) | |
| } else if (filter.limit) { | |
| this.$refs.filterDrawer.removeProjectLimit(filter) | |
| } else { | |
| delete this.filters[filter.key] | |
| this.$refs.filterDrawer.shownFilters = Object.keys(this.filters) | |
| this.$nextTick(() => { | |
| this.$refs.filterDrawer.filterData() | |
| }) | |
| } | |
| }, | |
| changeFilters (filters, loadedFilters) { | |
| this.filtersDrawerToggle = false | |
| this.loadedFilters = loadedFilters | |
| this.$emit('change-filters', filters) | |
| }, | |
| getSelectedRows () { | |
| const rows = this.dt.rows({ selected: true }) | |
| const result = [] | |
| const columnIndex = this.dt.column(`${this.batchSelect}:name`).index() | |
| const self = this | |
| rows.every(function () { | |
| result.push(self.dt.cell(this.index(), columnIndex).data()) | |
| }) | |
| return result | |
| }, | |
| getSelectedRowsActions () { | |
| const result = [] | |
| const rows = this.dt.rows({ selected: true }) | |
| rows.every(function () { | |
| let node = this.node() | |
| let data = null | |
| // handle dropdown actions | |
| let actions = $(node).find('.dt-action-buttons') | |
| if (actions.length > 0) { | |
| data = actions.find('li span').data() | |
| data.rawBtn = actions.html() | |
| } else { | |
| // handle single action btn | |
| let action = $(node).find('.dt-action') | |
| if (action.length > 0) { | |
| data = action.data() | |
| data.rawBtn = action.parent().html() | |
| } | |
| } | |
| if (data) { | |
| result.push(data) | |
| } | |
| }) | |
| return result.map(item => { | |
| return { | |
| id: item.id, | |
| type: item.type, | |
| rawBtn: item.rawBtn | |
| } | |
| }) | |
| }, | |
| reload (resetPaging = true) { | |
| if (this.dt) { | |
| this.dt.ajax.reload(() => {}, resetPaging) | |
| } | |
| }, | |
| randomId () { | |
| return Math.random() | |
| .toString(36) | |
| .substring(7) | |
| } | |
| } | |
| } | |
| </script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <template> | |
| <div> | |
| <rn-datatable | |
| ref="datatable" | |
| :url="dataTableUrl" | |
| :columns="visibleColumns" | |
| :show-export-buttons="true" | |
| :show-col-vis="true" | |
| :refresh-button="true" | |
| :custom-buttons="customButtons" | |
| :batch-select="batchSelect" | |
| :file-name="`${$tc('timesheets.timesheet_entries', 2)}`" | |
| :filters="filters" | |
| :filters-title="$tc('timesheets.timesheet_filters')" | |
| :constant-filters="constantFilters" | |
| :hide-filters="hideFilters" | |
| filters-type="timesheets" | |
| @change-filters="reload($event)" | |
| @approveItemEntry="approveTimesheetEntry($event)" | |
| @disapproveItemEntry="disapproveTimesheetEntry($event)" | |
| @finalApproveItemEntry="finalApproveTimesheetEntry($event)" | |
| @finalDisapproveItemEntry="finalDisapproveTimesheetEntry($event)" | |
| @deleteTimesheetEntry="deleteTimesheetEntry($event)" | |
| @editTimesheetEntry="editTimesheetEntry($event)" | |
| @historyTimesheetEntry="historyTimesheetEntry($event)"> | |
| <th | |
| v-for="column in visibleColumns" | |
| :key="column.name" | |
| :data-sortable="column.sortable" | |
| :data-priority="column.priority" | |
| :data-printable="column.printable !== undefined ? column.printable : 'yes'"> | |
| {{ column.text }} | |
| </th> | |
| </rn-datatable> | |
| <rn-mounter ref="manageTimesheet"> | |
| <rn-manage-timesheet | |
| :timesheet-for="timesheetFor" | |
| :timesheet="timesheetEdit" | |
| @close="$refs.manageTimesheet.dismount(); timesheetEdit = null" | |
| @newEntry="drawTable()" /> | |
| </rn-mounter> | |
| <rn-mounter ref="disapprovalNotes"> | |
| <rn-disapproval-notes | |
| :timesheet="timesheetNotes" | |
| :type="disapprovalType" | |
| @drawTable="drawTable()" | |
| @close="$refs.disapprovalNotes.dismount()" /> | |
| </rn-mounter> | |
| <rn-mounter ref="historyTimesheet"> | |
| <rn-timesheet-history | |
| :timesheet="timesheetHistory" | |
| @close="$refs.historyTimesheet.dismount()" /> | |
| </rn-mounter> | |
| </div> | |
| </template> | |
| <script> | |
| import _ from 'lodash' | |
| import axios from 'axios' | |
| import { omitDeep } from '../../helpers/lodash' | |
| import { mapState, mapGetters, mapActions } from 'vuex' | |
| import { handler } from 'errors' | |
| import rnMounter from '@components/utility/Mounter' | |
| import rnDatatable from '@components/datatables/Datatables' | |
| import rnManageTimesheet from '@/job-req/components/timesheets/ManageTimesheet' | |
| import rnDisapprovalNotes from '@/job-req/components/timesheets/DisapprovalNotes' | |
| import rnTimesheetHistory from '@/job-req/components/timesheets/TimesheetHistory' | |
| /* global PageLoaderEventBus */ | |
| export default { | |
| components: { | |
| rnMounter, | |
| rnDatatable, | |
| rnManageTimesheet, | |
| rnDisapprovalNotes, | |
| rnTimesheetHistory | |
| }, | |
| props: { | |
| noParentCol: { | |
| type: Boolean, | |
| default: false | |
| }, | |
| initialFilters: { | |
| type: Object, | |
| default: () => { return {} } | |
| }, | |
| constantFilters: { | |
| type: Object, | |
| default: null | |
| }, | |
| hideFilters: { | |
| type: Array, | |
| default: null | |
| }, | |
| timesheetFor: { | |
| type: Object, | |
| default: null | |
| } | |
| }, | |
| data () { | |
| return { | |
| disapprovalType: null, | |
| timesheetNotes: null, | |
| timesheetEdit: null, | |
| timesheetHistory: null, | |
| filters: this.initialFilters | |
| } | |
| }, | |
| computed: { | |
| ...mapState({ | |
| settings: state => state.settings.settings, | |
| currentUser: state => state.users.currentUser | |
| }), | |
| ...mapGetters({ | |
| getGlobalSetting: 'settings/global' | |
| }), | |
| categories () { | |
| return !this.getGlobalSetting('setting.billing.hide_categories', false) | |
| }, | |
| timsheetApprovalProcess () { | |
| return this.settings['setting.timesheets.final_approval_process'] | |
| }, | |
| dataTableUrl () { | |
| return '/timesheet-entries/table' | |
| }, | |
| batchSelect () { | |
| return 'timesheet.id' | |
| }, | |
| customButtons () { | |
| let buttons = [] | |
| let batchActionsBtns = [] | |
| let excelExportButtons = [] | |
| let results = [5, 10, 25, 50, 75, 100] | |
| if (this.$acl.hasSomePermissions(['timesheets.export'])) { | |
| excelExportButtons = excelExportButtons.concat([ | |
| { | |
| text: `<span class="batch-action">${this.$tc('timesheets.all_results')}</span>`, | |
| action: () => { | |
| this.exportExcel(-1) | |
| } | |
| } | |
| ]) | |
| for (const result of results) { | |
| excelExportButtons = excelExportButtons.concat([ | |
| { | |
| text: `<span class="batch-action">${result} ${this.$tc('timesheets.results')}</span>`, | |
| action: () => { | |
| this.exportExcel(result) | |
| } | |
| } | |
| ]) | |
| } | |
| } | |
| if (this.$acl.hasSomePermissions([ | |
| 'jobreqs.timesheets.approve', | |
| 'clientdev.timesheets.approve', | |
| 'project.timesheets.approve', | |
| 'sow.timesheets.approve', | |
| 'sow.scopes.timesheets.approve' | |
| ])) { | |
| batchActionsBtns = batchActionsBtns.concat([ | |
| { | |
| text: `<i class="fa fa-icon fa-thumbs-up"></i> <span class="batch-action">${this.$tc('timesheets.approve')}</span>`, | |
| action: () => { this.batchActions('approve') } | |
| }, | |
| { | |
| text: `<i class="fa fa-icon fa-thumbs-down"></i> <span class="batch-action">${this.$tc('timesheets.disapprove')}</span>`, | |
| action: () => { this.batchActions('disapprove') } | |
| } | |
| ]) | |
| } | |
| if (this.timsheetApprovalProcess) { | |
| if (this.$acl.hasSomePermissions([ | |
| 'jobreqs.timesheets.final_approve', | |
| 'clientdev.timesheets.final_approve', | |
| 'project.timesheets.final_approve', | |
| 'sow.timesheets.final_approve', | |
| 'sow.scopes.timesheets.final_approve' | |
| ])) { | |
| batchActionsBtns = batchActionsBtns.concat([ | |
| { | |
| text: `<i class="fa fa-icon fa-thumbs-up"></i> <span class="batch-action">${this.$tc('timesheets.final_approve')}</span>`, | |
| action: () => { this.batchActions('final-approve') } | |
| }, | |
| { | |
| text: `<i class="fa fa-icon fa-thumbs-down"></i> <span class="batch-action">${this.$tc('timesheets.final_disapprove')}</span>`, | |
| action: () => { this.batchActions('final-disapprove') } | |
| } | |
| ]) | |
| } | |
| } | |
| if (this.$acl.hasSomePermissions([ | |
| 'jobreqs.timesheets.delete', | |
| 'clientdev.timesheets.delete', | |
| 'project.timesheets.delete', | |
| 'sow.timesheets.delete', | |
| 'sow.scopes.timesheets.delete' | |
| ])) { | |
| batchActionsBtns = batchActionsBtns.concat([ | |
| { | |
| text: `<i class="fa fa-icon fa-trash"></i> <span class="batch-action">${this.$tc('general.delete')}</span>`, | |
| action: () => { this.batchActions('delete') } | |
| } | |
| ]) | |
| } | |
| if (this.timesheetFor | |
| ? this.timesheetFor.timesheets_permission | |
| : this.$acl.hasSomePermissions([ | |
| 'jobreqs.timesheets.create_all', | |
| 'clientdev.timesheets.create_all', | |
| 'project.timesheets.create_all', | |
| 'sow.timesheets.create_all', | |
| 'sow.scopes.timesheets.create_all' | |
| ])) { | |
| buttons = buttons.concat([ | |
| { | |
| extend: 'collection', | |
| text: `<i title="${this.$tc('timesheets.add_timesheet')}" data-toggle="tooltip" class="fa fa-plus"></i>`, | |
| autoClose: true, | |
| action: () => this.createTimesheetEntry() | |
| } | |
| ]) | |
| } | |
| if (batchActionsBtns.length > 0) { | |
| buttons = buttons.concat([ | |
| { | |
| extend: 'collection', | |
| text: `${this.$tc('timesheets.batch_actions')}`, | |
| autoClose: true, | |
| buttons: batchActionsBtns | |
| } | |
| ]) | |
| } | |
| if (excelExportButtons.length > 0) { | |
| buttons = buttons.concat([ | |
| { | |
| extend: 'collection', | |
| text: `${this.$tc('timesheets.excel_export')}`, | |
| autoClose: true, | |
| buttons: excelExportButtons | |
| } | |
| ]) | |
| } | |
| return buttons | |
| }, | |
| columns () { | |
| let columns = [ | |
| { | |
| name: 'timesheet.id', | |
| text: 'ID', | |
| canSee: true, | |
| visible: false, | |
| sortable: false, | |
| printable: 'no' | |
| }, | |
| { | |
| name: 'timesheet.created_by.contact.name[html:true]', | |
| text: `${this.$tc('timesheets.created_by')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.date', | |
| text: `${this.$tc('timesheets.date')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.contact.name[html:true]', | |
| text: `${this.$tc('timesheets.contact')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.parentType.name[html:true]', | |
| text: 'Timesheet For', | |
| canSee: true, | |
| sortable: false | |
| }, | |
| { | |
| name: 'timesheet.placement.job_req.category.name', | |
| text: `${this.$tc('timesheets.job_category')}`, | |
| canSee: true, | |
| sortable: false | |
| }, | |
| { | |
| name: 'timesheet.account_number', | |
| text: `${this.$tc('timesheets.account_number')}`, | |
| canSee: this.$acl.hasPermission('timesheets.account_number.view'), | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.resource_location', | |
| text: `Resource Location`, | |
| canSee: this.$acl.hasPermission('timesheets.resource_location.view'), | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.week_end_date', | |
| text: `${this.$tc('timesheets.week_end_date')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.hours', | |
| text: `${this.$tc('timesheets.hours')}`, | |
| canSee: true, | |
| sortable: true | |
| } | |
| ] | |
| if (this.$acl.userIsEmployee()) { | |
| if (this.$acl.hasPermission('jobreqs.compensation.bill_rate.view') || this.$acl.hasPermission('timesheets.view_rates')) { | |
| columns.push( | |
| { | |
| name: 'timesheet.bill_rate', | |
| text: `${this.$tc('timesheets.bill_rate')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.bill_rate_amount', | |
| text: `${this.$tc('timesheets.bill_rate_amount')}`, | |
| canSee: true, | |
| sortable: true | |
| } | |
| ) | |
| } | |
| if (this.$acl.hasPermission('timesheets.view_rates')) { | |
| columns.push( | |
| { | |
| name: 'timesheet.pay_rate', | |
| text: `${this.$tc('timesheets.pay_rate')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.pay_rate_amount', | |
| text: `${this.$tc('timesheets.pay_rate_amount')}`, | |
| canSee: true, | |
| sortable: true | |
| } | |
| ) | |
| } | |
| if (this.$acl.hasPermission('jobreqs.compensation.bill_rate.view') || this.$acl.hasPermission('timesheets.view_rates')) { | |
| columns.push( | |
| { | |
| name: 'timesheet.margin', | |
| text: `${this.$tc('timesheets.margin')}`, | |
| canSee: true, | |
| sortable: true | |
| } | |
| ) | |
| } | |
| columns.push({ | |
| name: 'timesheet.external_contact_id', | |
| text: `External Contact ID`, | |
| canSee: true, | |
| sortable: true | |
| }) | |
| } | |
| if (this.$acl.userIsHiringManager() && this.$acl.hasPermission('timesheets.view_rates')) { | |
| columns.push( | |
| { | |
| name: 'timesheet.bill_rate', | |
| text: `${this.$tc('timesheets.pay_rate')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.bill_rate_amount', | |
| text: `${this.$tc('timesheets.pay_rate_amount')}`, | |
| canSee: true, | |
| sortable: true | |
| } | |
| ) | |
| } | |
| if (this.$acl.userIsVendor() && this.$acl.hasPermission('timesheets.view_rates')) { | |
| columns.push( | |
| { | |
| name: 'timesheet.pay_rate', | |
| text: `${this.$tc('timesheets.bill_rate')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.pay_rate_amount', | |
| text: `${this.$tc('timesheets.bill_rate_amount')}`, | |
| canSee: true, | |
| sortable: true | |
| } | |
| ) | |
| } | |
| columns.push( | |
| { | |
| name: 'timesheet.adjusted_flag', | |
| text: `${this.$tc('timesheets.adjusted_flag')}`, | |
| canSee: this.$acl.userIsEmployee(), | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.post_approve_edited', | |
| text: `${this.$tc('timesheets.post_approve_edited')}`, | |
| canSee: this.$acl.userIsEmployee(), | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.status[html:true]', | |
| text: `${this.$tc('timesheets.status')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.approved_at[html:true]', | |
| text: `${this.$tc('timesheets.dis_approved_at')}`, | |
| canSee: true, | |
| sortable: false | |
| }, | |
| { | |
| name: 'timesheet.pay_type', | |
| text: `${this.$t('employee_portal.pay_type')}`, | |
| sortable: false | |
| }, | |
| { | |
| name: 'timesheet.category.name', | |
| text: `${this.$tc('employee_portal.category')}`, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.category.expense_type', | |
| text: `Expense Type`, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.vendor_recruiter.name[html:true]', | |
| text: `${this.$tc('timesheets.vendor_recruiter')}`, | |
| canSee: this.$acl.userIsEmployee(), | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.vendor_company.short_name[html:true]', | |
| text: `${this.$tc('timesheets.vendor_company')}`, | |
| canSee: this.$acl.userIsEmployee(), | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.reports_to.contact.name[html:true]', | |
| text: `${this.$tc('timesheets.reports_to')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.created_at', | |
| text: `${this.$tc('timesheets.created_at')}`, | |
| canSee: true, | |
| sortable: true | |
| }, | |
| { | |
| name: 'timesheet.invoice[html:false]', | |
| text: `Invoice`, | |
| canSee: this.$acl.hasPermission('billing.view_all'), | |
| sortable: false | |
| }, | |
| { | |
| name: 'timesheet.actions', | |
| text: `${this.$tc('timesheets.actions')}`, | |
| canSee: true, | |
| priority: 100, | |
| sortable: false, | |
| printable: 'no' | |
| } | |
| ) | |
| if (this.noParentCol) { | |
| let found = columns.find(item => item.name === 'timesheet.parentType.name[html:true]') | |
| let index = columns.indexOf(found) | |
| columns.splice(index, 1) | |
| } | |
| if (!this.categories) { | |
| let found = columns.find(item => item.name === 'timesheet.category.name') | |
| let index = columns.indexOf(found) | |
| columns.splice(index, 1) | |
| } | |
| return columns | |
| }, | |
| visibleColumns () { | |
| return this.columns.filter(column => column.canSee === true || column.canSee === undefined) | |
| } | |
| }, | |
| mounted () { | |
| this.loadSettings() | |
| }, | |
| methods: { | |
| ...mapActions({ | |
| loadSettings: 'settings/loadSettings', | |
| loadCategories: 'timesheets/categories/loadCategories' | |
| }), | |
| historyTimesheetEntry (data) { | |
| PageLoaderEventBus.$pageLoader.show() | |
| axios.get(`/timesheet-entries/${data.id}?includes=activity_log.log_entries.created_by.contact`) | |
| .then((response) => { | |
| this.timesheetHistory = response.data.data.activity_log.data.log_entries.data | |
| this.$refs.historyTimesheet.mount() | |
| }).catch(error => { | |
| handler(error) | |
| }) | |
| }, | |
| approveTimesheetEntry (data) { | |
| axios.post(`/timesheet-entries/approve/${data.id}`).then(() => { | |
| this.$refs.datatable.reload() | |
| }).catch(error => { | |
| handler(error) | |
| }) | |
| }, | |
| finalApproveTimesheetEntry (data) { | |
| axios.post(`/timesheet-entries/final-approve/${data.id}`).then(() => { | |
| this.$refs.datatable.reload() | |
| }).catch(error => { | |
| handler(error) | |
| }) | |
| }, | |
| disapproveTimesheetEntry (data) { | |
| this.timesheetNotes = data | |
| this.disapprovalType = null | |
| this.$refs.disapprovalNotes.mount() | |
| }, | |
| finalDisapproveTimesheetEntry (data) { | |
| this.timesheetNotes = data | |
| this.disapprovalType = 'final' | |
| this.$refs.disapprovalNotes.mount() | |
| }, | |
| createTimesheetEntry () { | |
| this.timesheetEdit = null | |
| this.$refs.manageTimesheet.mount() | |
| }, | |
| editTimesheetEntry (data) { | |
| this.loadCategories().then(() => { | |
| axios.get(`/timesheet-entries/${data.id}?includes=parent`).then(response => { | |
| this.timesheetEdit = response.data.data | |
| this.$refs.manageTimesheet.mount() | |
| }).catch(error => { | |
| handler(error) | |
| }) | |
| }) | |
| }, | |
| deleteTimesheetEntry (data) { | |
| this.$confirm(this.$tc('timesheets.timesheet_deletion_confirm'), { type: 'warning' }) | |
| .then(() => { | |
| axios.delete(`/timesheet-entries/${data.id}`).then(() => { | |
| this.$refs.datatable.reload() | |
| }).catch(error => { | |
| handler(error) | |
| }) | |
| }).catch(() => {}) | |
| }, | |
| reload (filters) { | |
| this.filters = _.assign({}, omitDeep(filters)) | |
| this.$nextTick(() => { | |
| this.$refs.datatable.reload() | |
| }) | |
| }, | |
| exportExcel (length) { | |
| const payload = JSON.parse(this.$refs.datatable.dt.ajax.params()) | |
| const columns = this.$refs.datatable.columns | |
| const displayColumns = [] | |
| const hideColumns = [] | |
| payload.length = length | |
| for (const [index, column] of columns.entries()) { | |
| if (column.bVisible == null || column.bVisible === true) { | |
| if (column.text !== 'Actions') { | |
| displayColumns.push(column.text) | |
| } else { | |
| hideColumns.push(index) | |
| } | |
| } else { | |
| hideColumns.push(index) | |
| } | |
| } | |
| payload.displayColumns = displayColumns | |
| payload.hideColumns = hideColumns | |
| axios.post(`/timesheet-entries/generate-xls`, payload, {responseType: 'blob'}) | |
| .then(response => { | |
| this.$message.success({ | |
| message: this.$tc('timesheets.excel_generated'), | |
| center: true, | |
| duration: 5000 | |
| }) | |
| }) | |
| .catch(error => { | |
| this.$message.success({ | |
| message: this.$tc('timesheets.excel_generated'), | |
| center: true, | |
| duration: 5000 | |
| }) | |
| }) | |
| }, | |
| batchActions (action) { | |
| let actionText = this.handleMsgText(action) | |
| this.$confirm( | |
| `${this.$t('timesheets.batch_action_msg', {action: actionText})}`, {type: 'info'}) | |
| .then(() => { | |
| let done = 0 | |
| const entries = JSON.parse(JSON.stringify(this.$refs.datatable.getSelectedRowsActions())) | |
| const promise = new Promise((resolve, reject) => { | |
| if (entries.length < 1) { | |
| this.$notify.error({ | |
| message: `${this.$tc('timesheets.batch_no_selection_msg')}`, | |
| duration: 3000 | |
| }) | |
| } | |
| entries.forEach(entry => { | |
| if ((entry.rawBtn.indexOf(`data-action="${action}"`) !== -1)) { | |
| if (action !== 'delete') { | |
| axios.post(`/timesheet-entries/${action}/${entry.id}`) | |
| .then(response => { | |
| resolve(response) | |
| }).catch(error => { | |
| handler(error) | |
| }) | |
| done++ | |
| } else { | |
| axios.delete(`/timesheet-entries/${entry.id}`) | |
| .then(response => { | |
| resolve(response) | |
| }).catch(error => { | |
| handler(error) | |
| }) | |
| done++ | |
| } | |
| } | |
| }) | |
| }) | |
| let msg = { | |
| message: `${done} / ${entries.length} items updated`, | |
| duration: 5000 | |
| } | |
| done === 0 ? this.$notify.error(msg) : this.$notify.info(msg) | |
| return promise | |
| }) | |
| .then(() => { | |
| this.drawTable() | |
| }) | |
| .catch(() => {}) | |
| }, | |
| handleMsgText (text) { | |
| switch (text) { | |
| case 'delete': | |
| return _.toLower(this.$tc('general.delete')) | |
| case 'approve': | |
| return _.toLower(this.$tc('timesheets.approve')) | |
| case 'disapprove': | |
| return _.toLower(this.$tc('timesheets.disapprove')) | |
| case 'final-approve': | |
| return this.$tc('timesheets_expenses.finally_approve') | |
| case 'final-disapprove': | |
| return this.$tc('timesheets_expenses.finally_disapprove') | |
| } | |
| }, | |
| drawTable () { | |
| this.$refs.datatable.reload(false) | |
| } | |
| } | |
| } | |
| </script> | |
| <style lang="scss" scoped> | |
| .batch-action { | |
| margin-left: 5px; | |
| display: inline-block; | |
| } | |
| </style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment