Skip to content

Instantly share code, notes, and snippets.

@branquito
Created April 11, 2019 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save branquito/8b742ab34a589d9c08f2436e0330416f to your computer and use it in GitHub Desktop.
Save branquito/8b742ab34a589d9c08f2436e0330416f to your computer and use it in GitHub Desktop.
datatables
<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>
<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