/home/bdqbpbxa/api-uniferx.goodface.com.ua/vendor/laravel/nova/resources/js/views/Detail.vue
<template>
<LoadingView :loading="initialLoading">
<template v-if="shouldOverrideMeta && resourceInformation && title">
<Head
:title="
__(':resource Details: :title', {
resource: resourceInformation.singularLabel,
title: title,
})
"
/>
</template>
<div v-if="shouldShowCards && hasDetailOnlyCards">
<Cards
v-if="cards.length > 0"
:cards="cards"
:only-on-detail="true"
:resource="resource"
:resource-id="resourceId"
:resource-name="resourceName"
/>
</div>
<!-- Resource Detail -->
<div
:class="{
'mt-6': shouldShowCards && hasDetailOnlyCards && cards.length > 0,
}"
:dusk="resourceName + '-detail-component'"
>
<component
:is="resolveComponentName(panel)"
v-for="panel in panels"
:key="panel.id"
:panel="panel"
:resource="resource"
:resource-id="resourceId"
:resource-name="resourceName"
class="mb-8"
>
<div v-if="panel.showToolbar" class="md:flex items-center mb-3">
<div class="flex flex-auto truncate items-center">
<Heading :level="1" v-text="panel.name" />
<Badge
v-if="resource.softDeleted"
:label="__('Soft Deleted')"
class="bg-red-100 text-red-500 dark:bg-red-400 dark:text-red-900 rounded px-2 py-0.5 ml-3"
/>
</div>
<div class="ml-auto flex items-center">
<!-- Actions Menu -->
<DetailActionDropdown
v-if="shouldShowActionDropdown"
:resource="resource"
:actions="actions"
:via-resource="viaResource"
:via-resource-id="viaResourceId"
:via-relationship="viaRelationship"
:resource-name="resourceName"
class="mt-1 md:mt-0 md:ml-2 md:mr-2"
@actionExecuted="actionExecuted"
@resource-deleted="getResource"
@resource-restored="getResource"
/>
<Link
v-if="showViewLink"
v-tooltip="{
placement: 'bottom',
distance: 10,
skidding: 0,
content: __('View'),
}"
:href="$url(`/resources/${resourceName}/${resourceId}`)"
class="rounded hover:bg-gray-200 dark:hover:bg-gray-800 focus:outline-none focus:ring"
data-testid="view-resource"
dusk="view-resource-button"
tabindex="1"
>
<BasicButton component="span">
<Icon type="eye" />
</BasicButton>
</Link>
<Link
v-if="resource.authorizedToUpdate"
v-tooltip="{
placement: 'bottom',
distance: 10,
skidding: 0,
content: __('Edit'),
}"
:href="$url(`/resources/${resourceName}/${resourceId}/edit`)"
class="rounded hover:bg-gray-200 dark:hover:bg-gray-800 focus:outline-none focus:ring"
data-testid="edit-resource"
dusk="edit-resource-button"
tabindex="1"
>
<BasicButton component="span">
<Icon type="pencil-alt" />
</BasicButton>
</Link>
</div>
</div>
</component>
</div>
</LoadingView>
</template>
<script>
import isNil from 'lodash/isNil'
import {
Errors,
HasCards,
InteractsWithResourceInformation,
mapProps,
} from '@/mixins'
import { minimum } from '@/util'
import { mapGetters, mapActions } from 'vuex'
export default {
props: {
shouldOverrideMeta: { type: Boolean, default: false },
showViewLink: { type: Boolean, default: false },
shouldEnableShortcut: { type: Boolean, default: false },
...mapProps([
'resourceName',
'resourceId',
'viaResource',
'viaResourceId',
'viaRelationship',
'relationshipType',
]),
},
mixins: [HasCards, InteractsWithResourceInformation],
data: () => ({
initialLoading: true,
loading: true,
title: null,
resource: null,
panels: [],
actions: [],
actionValidationErrors: new Errors(),
}),
/**
* Bind the keydown even listener when the component is created
*/
created() {
if (Nova.missingResource(this.resourceName)) return Nova.visit('/404')
if (this.shouldEnableShortcut === true) {
Nova.addShortcut('e', this.handleKeydown)
}
},
/**
* Unbind the keydown even listener when the before component is destroyed
*/
beforeUnmount() {
if (this.shouldEnableShortcut === true) {
Nova.disableShortcut('e')
}
},
/**
* Mount the component.
*/
mounted() {
this.initializeComponent()
},
methods: {
...mapActions(['startImpersonating']),
/**
* Initialize the component's data.
*/
handleResourceLoaded() {
this.loading = false
Nova.$emit('resource-loaded', {
resourceName: this.resourceName,
resourceId: this.resourceId.toString(),
mode: 'detail',
})
},
/**
* Handle the keydown event
*/
handleKeydown(e) {
if (
this.resource.authorizedToUpdate &&
e.target.tagName != 'INPUT' &&
e.target.tagName != 'TEXTAREA' &&
e.target.contentEditable != 'true'
) {
Nova.visit(`/resources/${this.resourceName}/${this.resourceId}/edit`)
}
},
/**
* Initialize the component's data.
*/
async initializeComponent() {
await this.getResource()
await this.getActions()
this.initialLoading = false
},
/**
* Get the resource information.
*/
getResource() {
this.loading = true
this.panels = null
this.resource = null
return minimum(
Nova.request().get(
'/nova-api/' + this.resourceName + '/' + this.resourceId,
{
params: {
viaResource: this.viaResource,
viaResourceId: this.viaResourceId,
viaRelationship: this.viaRelationship,
relationshipType: this.relationshipType,
},
}
)
)
.then(({ data: { title, panels, resource } }) => {
this.title = title
this.panels = panels
this.resource = resource
this.handleResourceLoaded()
})
.catch(error => {
if (error.response.status >= 500) {
Nova.$emit('error', error.response.data.message)
return
}
if (error.response.status === 404 && this.initialLoading) {
Nova.visit('/404')
return
}
if (error.response.status === 403) {
Nova.visit('/403')
return
}
if (error.response.status === 401) return Nova.redirectToLogin()
Nova.error(this.__('This resource no longer exists'))
Nova.visit(`/resources/${this.resourceName}`)
})
},
/**
* Get the available actions for the resource.
*/
async getActions() {
this.actions = []
try {
const response = await Nova.request().get(
'/nova-api/' + this.resourceName + '/actions',
{
params: {
resourceId: this.resourceId,
editing: true,
editMode: 'create',
display: 'detail',
},
}
)
this.actions = response.data?.actions
} catch (error) {
console.log(error)
Nova.error(this.__('Unable to load actions for this resource'))
}
},
/**
* Handle an action executed event.
*/
async actionExecuted() {
await this.getResource()
await this.getActions()
},
/**
* Resolve the component name.
*/
resolveComponentName(panel) {
return isNil(panel.prefixComponent) || panel.prefixComponent
? 'detail-' + panel.component
: panel.component
},
},
computed: {
...mapGetters(['currentUser']),
canBeImpersonated() {
return (
this.currentUser.canImpersonate && this.resource.authorizedToImpersonate
)
},
shouldShowActionDropdown() {
return (
this.resource && (this.actions.length > 0 || this.canModifyResource)
)
},
canModifyResource() {
return (
this.resource.authorizedToReplicate ||
this.canBeImpersonated ||
(this.resource.authorizedToDelete && !this.resource.softDeleted) ||
(this.resource.authorizedToRestore && this.resource.softDeleted) ||
this.resource.authorizedToForceDelete
)
},
/**
* Determine whether this is a detail view for an Action Event
*/
isActionDetail() {
return this.resourceName === 'action-events'
},
/**
* Get the endpoint for this resource's metrics.
*/
cardsEndpoint() {
return `/nova-api/${this.resourceName}/cards`
},
/**
* Get the extra card params to pass to the endpoint.
*/
extraCardParams() {
return {
resourceId: this.resourceId,
}
},
},
}
</script>