Created
April 11, 2019 13:56
-
-
Save branquito/8b742ab34a589d9c08f2436e0330416f to your computer and use it in GitHub Desktop.
datatables
This file contains 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 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