Files
2024-09-01 18:54:23 +05:00

335 lines
8.5 KiB
JavaScript

import { Errors } from '@/mixins'
import { computed, nextTick, reactive } from 'vue'
import each from 'lodash/each'
import find from 'lodash/find'
import filter from 'lodash/filter'
import isNil from 'lodash/isNil'
import isObject from 'lodash/isObject'
import map from 'lodash/map'
import tap from 'lodash/tap'
import trim from 'lodash/trim'
import { useLocalization } from '@/composables/useLocalization'
const { __ } = useLocalization()
export function useActions(props, emitter, store) {
const state = reactive({
working: false,
errors: new Errors(),
actionModalVisible: false,
responseModalVisible: false,
selectedActionKey: '',
endpoint: props.endpoint || `/nova-api/${props.resourceName}/action`,
actionResponseData: null,
})
const selectedResources = computed(() => props.selectedResources)
const selectedAction = computed(() => {
if (state.selectedActionKey) {
return find(allActions.value, a => a.uriKey === state.selectedActionKey)
}
})
const allActions = computed(() =>
props.actions.concat(props.pivotActions?.actions || [])
)
const encodedFilters = computed(
() => store.getters[`${props.resourceName}/currentEncodedFilters`]
)
const searchParameter = computed(() =>
props.viaRelationship
? props.viaRelationship + '_search'
: props.resourceName + '_search'
)
const currentSearch = computed(
() => store.getters.queryStringParams[searchParameter.value] || ''
)
const trashedParameter = computed(() =>
props.viaRelationship
? props.viaRelationship + '_trashed'
: props.resourceName + '_trashed'
)
const currentTrashed = computed(
() => store.getters.queryStringParams[trashedParameter.value] || ''
)
const availableActions = computed(() => {
return filter(
props.actions,
action => selectedResources.value.length > 0 && !action.standalone
)
})
const availablePivotActions = computed(() => {
if (!props.pivotActions) {
return []
}
return filter(props.pivotActions.actions, action => {
if (selectedResources.value.length === 0) {
return action.standalone
}
return true
})
})
const hasPivotActions = computed(() => availablePivotActions.value.length > 0)
const selectedActionIsPivotAction = computed(() => {
return (
hasPivotActions.value &&
Boolean(find(props.pivotActions.actions, a => a === selectedAction.value))
)
})
const actionRequestQueryString = computed(() => {
return {
action: state.selectedActionKey,
pivotAction: selectedActionIsPivotAction.value,
search: currentSearch.value,
filters: encodedFilters.value,
trashed: currentTrashed.value,
viaResource: props.viaResource,
viaResourceId: props.viaResourceId,
viaRelationship: props.viaRelationship,
}
})
const actionFormData = computed(() => {
return tap(new FormData(), formData => {
if (selectedResources.value === 'all') {
formData.append('resources', 'all')
} else {
let pivotIds = filter(
map(selectedResources.value, resource =>
isObject(resource) ? resource.id.pivotValue : null
)
)
each(selectedResources.value, resource =>
formData.append(
'resources[]',
isObject(resource) ? resource.id.value : resource
)
)
if (
selectedResources.value !== 'all' &&
selectedActionIsPivotAction.value === true &&
pivotIds.length > 0
) {
each(pivotIds, pivotId => formData.append('pivots[]', pivotId))
}
}
each(selectedAction.value.fields, field => {
field.fill(formData)
})
})
})
function determineActionStrategy() {
if (selectedAction.value.withoutConfirmation) {
executeAction()
} else {
openConfirmationModal()
}
}
function openConfirmationModal() {
state.actionModalVisible = true
}
function closeConfirmationModal() {
state.actionModalVisible = false
}
function openResponseModal() {
state.responseModalVisible = true
}
function closeResponseModal() {
state.responseModalVisible = false
}
function emitResponseCallback(callback) {
emitter('actionExecuted')
Nova.$emit('action-executed')
if (typeof callback === 'function') {
callback()
}
}
function showActionResponseMessage(data) {
if (data.danger) {
return Nova.error(data.danger)
}
Nova.success(data.message || __('The action was executed successfully.'))
}
function executeAction(then) {
state.working = true
Nova.$progress.start()
let responseType = selectedAction.value.responseType ?? 'json'
Nova.request({
method: 'post',
url: state.endpoint,
params: actionRequestQueryString.value,
data: actionFormData.value,
responseType,
})
.then(async response => {
closeConfirmationModal()
handleActionResponse(response.data, response.headers, then)
})
.catch(error => {
if (error.response && error.response.status === 422) {
if (responseType === 'blob') {
error.response.data.text().then(data => {
state.errors = new Errors(JSON.parse(data).errors)
})
} else {
state.errors = new Errors(error.response.data.errors)
}
Nova.error(__('There was a problem executing the action.'))
}
})
.finally(() => {
state.working = false
Nova.$progress.done()
})
}
function handleActionResponse(data, headers, then) {
let contentDisposition = headers['content-disposition']
if (
data instanceof Blob &&
isNil(contentDisposition) &&
data.type === 'application/json'
) {
data.text().then(jsonStringData => {
handleActionResponse(JSON.parse(jsonStringData), headers)
})
return
}
if (data instanceof Blob) {
return emitResponseCallback(async () => {
let fileName = 'unknown'
if (contentDisposition) {
let fileNameMatch = contentDisposition
.split(';')[1]
.match(/filename=(.+)/)
if (fileNameMatch.length === 2) fileName = trim(fileNameMatch[1], '"')
}
await nextTick(() => {
let url = window.URL.createObjectURL(new Blob([data]))
let link = document.createElement('a')
link.href = url
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
link.remove()
window.URL.revokeObjectURL(url)
})
})
}
if (data.modal) {
state.actionResponseData = data
showActionResponseMessage(data)
return openResponseModal()
}
if (data.download) {
return emitResponseCallback(async () => {
showActionResponseMessage(data)
await nextTick(() => {
let link = document.createElement('a')
link.href = data.download
link.download = data.name
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
})
})
}
if (data.deleted) {
return emitResponseCallback(() => showActionResponseMessage(data))
}
if (data.redirect) {
window.location = data.redirect
}
if (data.visit) {
showActionResponseMessage(data)
return Nova.visit({
url: Nova.url(data.visit.path, data.visit.options),
remote: false,
})
}
if (data.openInNewTab) {
return emitResponseCallback(() =>
window.open(data.openInNewTab, '_blank')
)
}
emitResponseCallback(() => showActionResponseMessage(data))
}
function handleActionClick(uriKey) {
state.selectedActionKey = uriKey
determineActionStrategy()
}
function setSelectedActionKey(key) {
state.selectedActionKey = key
}
return {
errors: computed(() => state.errors),
working: computed(() => state.working),
actionModalVisible: computed(() => state.actionModalVisible),
responseModalVisible: computed(() => state.responseModalVisible),
selectedActionKey: computed(() => state.selectedActionKey),
determineActionStrategy,
setSelectedActionKey,
openConfirmationModal,
closeConfirmationModal,
openResponseModal,
closeResponseModal,
handleActionClick,
selectedAction,
allActions,
availableActions,
availablePivotActions,
executeAction,
actionResponseData: computed(() => state.actionResponseData),
}
}