995 lines
27 KiB
Vue
Executable File
995 lines
27 KiB
Vue
Executable File
<template>
|
|
<loading-view :loading="initialLoading" :dusk="lens + '-lens-component'">
|
|
<custom-lens-header class="mb-3" :resource-name="resourceName" />
|
|
|
|
<div v-if="shouldShowCards">
|
|
<cards
|
|
v-if="smallCards.length > 0"
|
|
:cards="smallCards"
|
|
class="mb-3"
|
|
:resource-name="resourceName"
|
|
:lens="lens"
|
|
/>
|
|
|
|
<cards
|
|
v-if="largeCards.length > 0"
|
|
:cards="largeCards"
|
|
size="large"
|
|
:resource-name="resourceName"
|
|
:lens="lens"
|
|
/>
|
|
</div>
|
|
|
|
<heading v-if="resourceResponse" class="mb-3">
|
|
<router-link
|
|
:to="{
|
|
name: 'index',
|
|
params: {
|
|
resourceName: resourceName,
|
|
},
|
|
}"
|
|
class="no-underline text-primary font-bold dim"
|
|
data-testid="lens-back"
|
|
>←</router-link
|
|
>
|
|
|
|
<span class="px-2 text-70">/</span> {{ lenseName }}
|
|
</heading>
|
|
|
|
<card>
|
|
<div class="py-3 flex items-center border-b border-50">
|
|
<div class="px-3" v-if="shouldShowCheckBoxes">
|
|
<!-- Select All -->
|
|
<dropdown
|
|
dusk="select-all-dropdown"
|
|
placement="bottom-end"
|
|
class="-mx-2"
|
|
>
|
|
<dropdown-trigger class="px-2">
|
|
<fake-checkbox :checked="selectAllChecked" />
|
|
</dropdown-trigger>
|
|
|
|
<dropdown-menu slot="menu" direction="ltr" width="250">
|
|
<div class="p-4">
|
|
<ul class="list-reset">
|
|
<li class="flex items-center mb-4">
|
|
<checkbox-with-label
|
|
:checked="selectAllChecked"
|
|
@input="toggleSelectAll"
|
|
>
|
|
{{ __('Select All') }}
|
|
</checkbox-with-label>
|
|
</li>
|
|
<li
|
|
class="flex items-center"
|
|
v-if="allMatchingResourceCount > 0"
|
|
>
|
|
<checkbox-with-label
|
|
dusk="select-all-matching-button"
|
|
:checked="selectAllMatchingChecked"
|
|
@input="toggleSelectAllMatching"
|
|
>
|
|
<template>
|
|
<span class="mr-1">
|
|
{{ __('Select All Matching') }} ({{
|
|
allMatchingResourceCount
|
|
}})
|
|
</span>
|
|
</template>
|
|
</checkbox-with-label>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</dropdown-menu>
|
|
</dropdown>
|
|
</div>
|
|
|
|
<div class="flex items-center ml-auto px-3">
|
|
<!-- Action Selector -->
|
|
<action-selector
|
|
v-if="selectedResources.length > 0 || haveStandaloneActions"
|
|
:resource-name="resourceName"
|
|
:actions="availableActions"
|
|
:pivot-actions="pivotActions"
|
|
:pivot-name="pivotName"
|
|
:selected-resources="selectedResourcesForActionSelector"
|
|
:endpoint="lensActionEndpoint"
|
|
:query-string="{
|
|
currentSearch,
|
|
encodedFilters,
|
|
currentTrashed,
|
|
viaResource,
|
|
viaResourceId,
|
|
viaRelationship,
|
|
}"
|
|
@actionExecuted="getResources"
|
|
/>
|
|
|
|
<filter-menu
|
|
:resourceName="resourceName"
|
|
:soft-deletes="softDeletes"
|
|
:via-resource="viaResource"
|
|
:via-has-one="viaHasOne"
|
|
:trashed="trashed"
|
|
:per-page="perPage"
|
|
:per-page-options="
|
|
perPageOptions || resourceInformation.perPageOptions
|
|
"
|
|
:show-trashed-option="
|
|
authorizedToForceDeleteAnyResources ||
|
|
authorizedToRestoreAnyResources
|
|
"
|
|
:lens="lens"
|
|
@clear-selected-filters="clearSelectedFilters(lens)"
|
|
@filter-changed="filterChanged"
|
|
@trashed-changed="trashedChanged"
|
|
@per-page-changed="updatePerPageChanged"
|
|
/>
|
|
|
|
<delete-menu
|
|
v-if="shouldShowDeleteMenu"
|
|
dusk="delete-menu"
|
|
:soft-deletes="softDeletes"
|
|
:resources="resources"
|
|
:selected-resources="selectedResources"
|
|
:via-many-to-many="viaManyToMany"
|
|
:all-matching-resource-count="allMatchingResourceCount"
|
|
:all-matching-selected="selectAllMatchingChecked"
|
|
:authorized-to-delete-selected-resources="
|
|
authorizedToDeleteSelectedResources
|
|
"
|
|
:authorized-to-force-delete-selected-resources="
|
|
authorizedToForceDeleteSelectedResources
|
|
"
|
|
:authorized-to-delete-any-resources="authorizedToDeleteAnyResources"
|
|
:authorized-to-force-delete-any-resources="
|
|
authorizedToForceDeleteAnyResources
|
|
"
|
|
:authorized-to-restore-selected-resources="
|
|
authorizedToRestoreSelectedResources
|
|
"
|
|
:authorized-to-restore-any-resources="
|
|
authorizedToRestoreAnyResources
|
|
"
|
|
@deleteSelected="deleteSelectedResources"
|
|
@deleteAllMatching="deleteAllMatchingResources"
|
|
@forceDeleteSelected="forceDeleteSelectedResources"
|
|
@forceDeleteAllMatching="forceDeleteAllMatchingResources"
|
|
@restoreSelected="restoreSelectedResources"
|
|
@restoreAllMatching="restoreAllMatchingResources"
|
|
@close="deleteModalOpen = false"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<loading-view :loading="loading">
|
|
<div
|
|
v-if="!resources.length"
|
|
class="flex justify-center items-center px-6 py-8"
|
|
>
|
|
<div class="text-center">
|
|
<svg
|
|
class="mb-3"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="65"
|
|
height="51"
|
|
viewBox="0 0 65 51"
|
|
>
|
|
<path
|
|
fill="#A8B9C5"
|
|
d="M56 40h2c.552285 0 1 .447715 1 1s-.447715 1-1 1h-2v2c0 .552285-.447715 1-1 1s-1-.447715-1-1v-2h-2c-.552285 0-1-.447715-1-1s.447715-1 1-1h2v-2c0-.552285.447715-1 1-1s1 .447715 1 1v2zm-5.364125-8H38v8h7.049375c.350333-3.528515 2.534789-6.517471 5.5865-8zm-5.5865 10H6c-3.313708 0-6-2.686292-6-6V6c0-3.313708 2.686292-6 6-6h44c3.313708 0 6 2.686292 6 6v25.049375C61.053323 31.5511 65 35.814652 65 41c0 5.522847-4.477153 10-10 10-5.185348 0-9.4489-3.946677-9.950625-9zM20 30h16v-8H20v8zm0 2v8h16v-8H20zm34-2v-8H38v8h16zM2 30h16v-8H2v8zm0 2v4c0 2.209139 1.790861 4 4 4h12v-8H2zm18-12h16v-8H20v8zm34 0v-8H38v8h16zM2 20h16v-8H2v8zm52-10V6c0-2.209139-1.790861-4-4-4H6C3.790861 2 2 3.790861 2 6v4h52zm1 39c4.418278 0 8-3.581722 8-8s-3.581722-8-8-8-8 3.581722-8 8 3.581722 8 8 8z"
|
|
/>
|
|
</svg>
|
|
|
|
<h3 class="text-base text-80 font-normal mb-6">
|
|
{{
|
|
__('No :resource matched the given criteria.', {
|
|
resource: resourceInformation.label.toLowerCase(),
|
|
})
|
|
}}
|
|
</h3>
|
|
|
|
<create-resource-button
|
|
classes="btn btn-sm btn-outline"
|
|
:singular-name="singularName"
|
|
:resource-name="resourceName"
|
|
:via-resource="viaResource"
|
|
:via-resource-id="viaResourceId"
|
|
:via-relationship="viaRelationship"
|
|
:relationship-type="relationshipType"
|
|
:authorized-to-create="authorizedToCreate && !resourceIsFull"
|
|
:authorized-to-relate="authorizedToRelate"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resource Table -->
|
|
<div class="overflow-hidden overflow-x-auto relative">
|
|
<resource-table
|
|
:authorized-to-relate="authorizedToRelate"
|
|
:resource-name="resourceName"
|
|
:resources="resources"
|
|
:singular-name="singularName"
|
|
:selected-resources="selectedResources"
|
|
:selected-resource-ids="selectedResourceIds"
|
|
:actions-are-available="allActions.length > 0"
|
|
:actions-endpoint="lensActionEndpoint"
|
|
:should-show-checkboxes="shouldShowCheckBoxes"
|
|
:via-resource="viaResource"
|
|
:via-resource-id="viaResourceId"
|
|
:via-relationship="viaRelationship"
|
|
:relationship-type="relationshipType"
|
|
:update-selection-status="updateSelectionStatus"
|
|
@order="orderByField"
|
|
@reset-order-by="resetOrderBy"
|
|
@delete="deleteResources"
|
|
@restore="restoreResources"
|
|
@actionExecuted="getResources"
|
|
ref="resourceTable"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<component
|
|
:is="paginationComponent"
|
|
v-if="resourceResponse && resources.length > 0"
|
|
:next="hasNextPage"
|
|
:previous="hasPreviousPage"
|
|
@load-more="loadMore"
|
|
@page="selectPage"
|
|
:pages="totalPages"
|
|
:page="currentPage"
|
|
:per-page="perPage"
|
|
:resource-count-label="resourceCountLabel"
|
|
:current-resource-count="resources.length"
|
|
:all-matching-resource-count="allMatchingResourceCount"
|
|
>
|
|
<span
|
|
v-if="resourceCountLabel"
|
|
class="text-sm text-80 px-4"
|
|
:class="{
|
|
'ml-auto': paginationComponent == 'pagination-links',
|
|
}"
|
|
>
|
|
{{ resourceCountLabel }}
|
|
</span>
|
|
</component>
|
|
</loading-view>
|
|
</card>
|
|
</loading-view>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
HasCards,
|
|
Deletable,
|
|
Errors,
|
|
Filterable,
|
|
Minimum,
|
|
Paginatable,
|
|
PerPageable,
|
|
InteractsWithQueryString,
|
|
InteractsWithResourceInformation,
|
|
} from 'laravel-nova'
|
|
import HasActions from '@/mixins/HasActions'
|
|
import { CancelToken, Cancel } from 'axios'
|
|
|
|
export default {
|
|
mixins: [
|
|
HasActions,
|
|
HasCards,
|
|
Deletable,
|
|
Filterable,
|
|
Paginatable,
|
|
PerPageable,
|
|
InteractsWithResourceInformation,
|
|
InteractsWithQueryString,
|
|
],
|
|
|
|
metaInfo() {
|
|
return {
|
|
title: this.lenseName,
|
|
}
|
|
},
|
|
|
|
props: {
|
|
resourceName: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
|
|
viaResource: {
|
|
default: '',
|
|
},
|
|
|
|
viaResourceId: {
|
|
default: '',
|
|
},
|
|
|
|
viaRelationship: {
|
|
default: '',
|
|
},
|
|
|
|
relationshipType: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
|
|
lens: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
},
|
|
|
|
data: () => ({
|
|
canceller: null,
|
|
initialLoading: true,
|
|
loading: true,
|
|
|
|
resourceResponse: null,
|
|
resources: [],
|
|
softDeletes: false,
|
|
selectedResources: [],
|
|
selectAllMatchingResources: false,
|
|
allMatchingResourceCount: 0,
|
|
hasId: false,
|
|
|
|
deleteModalOpen: false,
|
|
|
|
actionValidationErrors: new Errors(),
|
|
|
|
authorizedToRelate: false,
|
|
|
|
orderBy: '',
|
|
orderByDirection: '',
|
|
trashed: '',
|
|
|
|
// Load More Pagination
|
|
currentPageLoadMore: null,
|
|
}),
|
|
|
|
/**
|
|
* Mount the component and retrieve its initial data.
|
|
*/
|
|
async created() {
|
|
if (Nova.missingResource(this.resourceName))
|
|
return this.$router.push({ name: '404' })
|
|
|
|
this.initializeSearchFromQueryString()
|
|
this.initializePerPageFromQueryString()
|
|
this.initializeTrashedFromQueryString()
|
|
this.initializeOrderingFromQueryString()
|
|
|
|
await this.initializeFilters(this.lens)
|
|
this.getResources()
|
|
// this.getAuthorizationToRelate()
|
|
this.getActions()
|
|
|
|
this.initialLoading = false
|
|
|
|
this.$watch(
|
|
() => {
|
|
return (
|
|
this.lens +
|
|
this.resourceName +
|
|
this.encodedFilters +
|
|
this.currentSearch +
|
|
this.currentPage +
|
|
this.currentPerPage +
|
|
this.currentOrderBy +
|
|
this.currentOrderByDirection +
|
|
this.currentTrashed
|
|
)
|
|
},
|
|
() => {
|
|
if (this.canceller !== null) this.canceller()
|
|
|
|
this.getResources()
|
|
}
|
|
)
|
|
},
|
|
|
|
watch: {
|
|
$route(to, from) {
|
|
if (
|
|
to.params.resourceName === from.params.resourceName &&
|
|
to.params.lens === from.params.lens
|
|
) {
|
|
this.initializeState(this.lens)
|
|
} else {
|
|
this.initializeFilters(this.lens)
|
|
this.getActions()
|
|
}
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
selectAllResources() {
|
|
this.selectedResources = this.resources.slice(0)
|
|
},
|
|
|
|
toggleSelectAll() {
|
|
if (this.selectAllChecked) return this.clearResourceSelections()
|
|
this.selectAllResources()
|
|
},
|
|
|
|
toggleSelectAllMatching() {
|
|
if (!this.selectAllMatchingResources) {
|
|
this.selectAllResources()
|
|
this.selectAllMatchingResources = true
|
|
|
|
return
|
|
}
|
|
|
|
this.selectAllMatchingResources = false
|
|
},
|
|
|
|
/*
|
|
* Update the resource selection status
|
|
*/
|
|
updateSelectionStatus(resource) {
|
|
if (!_(this.selectedResources).includes(resource))
|
|
return this.selectedResources.push(resource)
|
|
const index = this.selectedResources.indexOf(resource)
|
|
if (index > -1) return this.selectedResources.splice(index, 1)
|
|
},
|
|
|
|
/**
|
|
* Get the resources based on the current page, search, filters, etc.
|
|
*/
|
|
getResources() {
|
|
this.loading = true
|
|
|
|
this.$nextTick(() => {
|
|
this.clearResourceSelections()
|
|
|
|
return Minimum(
|
|
Nova.request().get(
|
|
'/nova-api/' + this.resourceName + '/lens/' + this.lens,
|
|
{
|
|
params: this.resourceRequestQueryString,
|
|
cancelToken: new CancelToken(canceller => {
|
|
this.canceller = canceller
|
|
}),
|
|
}
|
|
),
|
|
300
|
|
)
|
|
.then(({ data }) => {
|
|
this.resources = []
|
|
|
|
this.resourceResponse = data
|
|
this.resources = data.resources
|
|
this.softDeletes = data.softDeletes
|
|
this.perPage = data.per_page
|
|
this.hasId = data.hasId
|
|
|
|
this.loading = false
|
|
|
|
this.getAllMatchingResourceCount()
|
|
|
|
Nova.$emit('resources-loaded')
|
|
})
|
|
.catch(e => {
|
|
if (e instanceof Cancel) {
|
|
return
|
|
}
|
|
|
|
throw e
|
|
})
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Get the actions available for the current resource.
|
|
*/
|
|
getActions() {
|
|
this.actions = []
|
|
this.pivotActions = null
|
|
Nova.request()
|
|
.get(`/nova-api/${this.resourceName}/lens/${this.lens}/actions`, {
|
|
params: {
|
|
viaResource: this.viaResource,
|
|
viaResourceId: this.viaResourceId,
|
|
viaRelationship: this.viaRelationship,
|
|
relationshipType: this.relationshipType,
|
|
display: 'index',
|
|
},
|
|
})
|
|
.then(response => {
|
|
this.actions = response.data.actions
|
|
this.pivotActions = response.data.pivotActions
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Clear the selected resouces and the "select all" states.
|
|
*/
|
|
clearResourceSelections() {
|
|
this.selectAllMatchingResources = false
|
|
this.selectedResources = []
|
|
},
|
|
|
|
/**
|
|
* Get the count of all of the matching resources.
|
|
*/
|
|
getAllMatchingResourceCount() {
|
|
Nova.request()
|
|
.get(
|
|
'/nova-api/' + this.resourceName + '/lens/' + this.lens + '/count',
|
|
{
|
|
params: this.resourceRequestQueryString,
|
|
}
|
|
)
|
|
.then(response => {
|
|
this.allMatchingResourceCount = response.data.count
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Sort the resources by the given field.
|
|
*/
|
|
orderByField(field) {
|
|
let direction = this.currentOrderByDirection == 'asc' ? 'desc' : 'asc'
|
|
|
|
if (this.currentOrderBy != field.sortableUriKey) {
|
|
direction = 'asc'
|
|
}
|
|
|
|
this.updateQueryString({
|
|
[this.orderByParameter]: field.sortableUriKey,
|
|
[this.orderByDirectionParameter]: direction,
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Reset the order by to its default state
|
|
*/
|
|
resetOrderBy(field) {
|
|
this.updateQueryString({
|
|
[this.orderByParameter]: field.sortableUriKey,
|
|
[this.orderByDirectionParameter]: null,
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Sync the current search value from the query string.
|
|
*/
|
|
initializeSearchFromQueryString() {
|
|
this.search = this.currentSearch
|
|
},
|
|
|
|
/**
|
|
* Sync the current order by values from the query string.
|
|
*/
|
|
initializeOrderingFromQueryString() {
|
|
this.orderBy = this.currentOrderBy
|
|
this.orderByDirection = this.currentOrderByDirection
|
|
},
|
|
|
|
/**
|
|
* Sync the trashed state values from the query string.
|
|
*/
|
|
initializeTrashedFromQueryString() {
|
|
this.trashed = this.currentTrashed
|
|
},
|
|
|
|
/**
|
|
* Update the trashed constraint for the resource listing.
|
|
*/
|
|
trashedChanged(trashedStatus) {
|
|
this.trashed = trashedStatus
|
|
this.updateQueryString({ [this.trashedParameter]: this.trashed })
|
|
},
|
|
|
|
/**
|
|
* Update the per page parameter in the query string
|
|
*/
|
|
updatePerPageChanged(perPage) {
|
|
this.perPage = perPage
|
|
this.perPageChanged()
|
|
},
|
|
|
|
/**
|
|
* Load more resources.
|
|
*/
|
|
loadMore() {
|
|
if (this.currentPageLoadMore === null) {
|
|
this.currentPageLoadMore = this.currentPage
|
|
}
|
|
|
|
this.currentPageLoadMore = this.currentPageLoadMore + 1
|
|
|
|
return Minimum(
|
|
Nova.request().get(
|
|
'/nova-api/' + this.resourceName + '/lens/' + this.lens,
|
|
{
|
|
params: {
|
|
...this.resourceRequestQueryString,
|
|
page: this.currentPageLoadMore, // We do this to override whatever page number is in the URL
|
|
},
|
|
}
|
|
),
|
|
300
|
|
).then(({ data }) => {
|
|
this.resourceResponse = data
|
|
this.resources = [...this.resources, ...data.resources]
|
|
|
|
this.getAllMatchingResourceCount()
|
|
|
|
Nova.$emit('resources-loaded')
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Select the next page.
|
|
*/
|
|
selectPage(page) {
|
|
this.updateQueryString({ [this.pageParameter]: page })
|
|
},
|
|
|
|
/**
|
|
* Sync the per page values from the query string.
|
|
*/
|
|
initializePerPageFromQueryString() {
|
|
this.perPage =
|
|
this.$route.query[this.perPageParameter] ||
|
|
this.resourceInformation.perPageOptions[0]
|
|
},
|
|
},
|
|
|
|
computed: {
|
|
/**
|
|
* Get the endpoint for this resource's actions.
|
|
*/
|
|
lensActionEndpoint() {
|
|
return `/nova-api/${this.resourceName}/lens/${this.lens}/action`
|
|
},
|
|
|
|
/**
|
|
* Get the name of the search query string variable.
|
|
*/
|
|
searchParameter() {
|
|
return this.resourceName + '_search'
|
|
},
|
|
|
|
/**
|
|
* Get the name of the order by query string variable.
|
|
*/
|
|
orderByParameter() {
|
|
return this.resourceName + '_order'
|
|
},
|
|
|
|
/**
|
|
* Get the name of the order by direction query string variable.
|
|
*/
|
|
orderByDirectionParameter() {
|
|
return this.resourceName + '_direction'
|
|
},
|
|
|
|
/**
|
|
* Get the name of the trashed constraint query string variable.
|
|
*/
|
|
trashedParameter() {
|
|
return this.resourceName + '_trashed'
|
|
},
|
|
|
|
/**
|
|
* Get the name of the per page query string variable.
|
|
*/
|
|
perPageParameter() {
|
|
return this.resourceName + '_per_page'
|
|
},
|
|
|
|
/**
|
|
* Get the name of the page query string variable.
|
|
*/
|
|
pageParameter() {
|
|
return this.resourceName + '_page'
|
|
},
|
|
|
|
/**
|
|
* Build the resource request query string.
|
|
*/
|
|
resourceRequestQueryString() {
|
|
return {
|
|
search: this.currentSearch,
|
|
filters: this.encodedFilters,
|
|
orderBy: this.currentOrderBy,
|
|
orderByDirection: this.currentOrderByDirection,
|
|
perPage: this.currentPerPage,
|
|
page: this.currentPage,
|
|
viaResource: this.viaResource,
|
|
viaResourceId: this.viaResourceId,
|
|
// viaRelationship: this.viaRelationship,
|
|
viaResourceRelationship: this.viaResourceRelationship,
|
|
relationshipType: this.relationshipType,
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Determine if all resources are selected.
|
|
*/
|
|
selectAllChecked() {
|
|
return this.selectedResources.length == this.resources.length
|
|
},
|
|
|
|
/**
|
|
* Determine if all matching resources are selected.
|
|
*/
|
|
selectAllMatchingChecked() {
|
|
return (
|
|
this.selectedResources.length == this.resources.length &&
|
|
this.selectAllMatchingResources
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Get the IDs for the selected resources.
|
|
*/
|
|
selectedResourceIds() {
|
|
return _.map(this.selectedResources, resource => resource.id.value)
|
|
},
|
|
|
|
/**
|
|
* Get the current search value from the query string.
|
|
*/
|
|
currentSearch() {
|
|
return this.$route.query[this.searchParameter] || ''
|
|
},
|
|
|
|
/**
|
|
* Get the current order by value from the query string.
|
|
*/
|
|
currentOrderBy() {
|
|
return this.$route.query[this.orderByParameter] || ''
|
|
},
|
|
|
|
/**
|
|
* Get the current order by direction from the query string.
|
|
*/
|
|
currentOrderByDirection() {
|
|
return this.$route.query[this.orderByDirectionParameter] || 'desc'
|
|
},
|
|
|
|
/**
|
|
* Get the current trashed constraint value from the query string.
|
|
*/
|
|
currentTrashed() {
|
|
return this.$route.query[this.trashedParameter] || ''
|
|
},
|
|
|
|
/**
|
|
* Determine if the current resource listing is via a many-to-many relationship.
|
|
*/
|
|
viaManyToMany() {
|
|
return (
|
|
this.relationshipType == 'belongsToMany' ||
|
|
this.relationshipType == 'morphToMany'
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Determine if the resource / relationship is "full".
|
|
*/
|
|
resourceIsFull() {
|
|
return this.viaHasOne && this.resources.length > 0
|
|
},
|
|
|
|
/**
|
|
* Determine if the current resource listing is via a has-one relationship.
|
|
*/
|
|
viaHasOne() {
|
|
return (
|
|
this.relationshipType == 'hasOne' || this.relationshipType == 'morphOne'
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Get the singular name for the resource
|
|
*/
|
|
singularName() {
|
|
return this.resourceInformation.singularLabel
|
|
},
|
|
|
|
/**
|
|
* Determine if there are any resources for the view
|
|
*/
|
|
hasResources() {
|
|
return Boolean(this.resources.length > 0)
|
|
},
|
|
|
|
/**
|
|
* Determine if the resource should show any cards
|
|
*/
|
|
shouldShowCards() {
|
|
return this.cards.length > 0
|
|
},
|
|
|
|
/**
|
|
* Get the endpoint for this resource's metrics.
|
|
*/
|
|
cardsEndpoint() {
|
|
return `/nova-api/${this.resourceName}/lens/${this.lens}/cards`
|
|
},
|
|
|
|
/**
|
|
* Determine whether to show the selection checkboxes for resources
|
|
*/
|
|
shouldShowCheckBoxes() {
|
|
return (
|
|
Boolean(this.hasResources && !this.viaHasOne) &&
|
|
Boolean(
|
|
this.actionsAreAvailable ||
|
|
this.authorizedToDeleteAnyResources ||
|
|
this.canShowDeleteMenu
|
|
)
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Determine whether the delete menu should be shown to the user
|
|
*/
|
|
shouldShowDeleteMenu() {
|
|
return (
|
|
Boolean(this.selectedResources.length > 0) && this.canShowDeleteMenu
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Determine if any selected resources may be deleted.
|
|
*/
|
|
authorizedToDeleteSelectedResources() {
|
|
return Boolean(
|
|
_.find(this.selectedResources, resource => resource.authorizedToDelete)
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Determine if any selected resources may be force deleted.
|
|
*/
|
|
authorizedToForceDeleteSelectedResources() {
|
|
return Boolean(
|
|
_.find(
|
|
this.selectedResources,
|
|
resource => resource.authorizedToForceDelete
|
|
)
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Determine if the user is authorized to delete any listed resource.
|
|
*/
|
|
authorizedToDeleteAnyResources() {
|
|
return (
|
|
this.resources.length > 0 &&
|
|
Boolean(_.find(this.resources, resource => resource.authorizedToDelete))
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Determine if the user is authorized to force delete any listed resource.
|
|
*/
|
|
authorizedToForceDeleteAnyResources() {
|
|
return (
|
|
this.resources.length > 0 &&
|
|
Boolean(
|
|
_.find(this.resources, resource => resource.authorizedToForceDelete)
|
|
)
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Determine if any selected resources may be restored.
|
|
*/
|
|
authorizedToRestoreSelectedResources() {
|
|
return Boolean(
|
|
_.find(this.selectedResources, resource => resource.authorizedToRestore)
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Determine if the user is authorized to restore any listed resource.
|
|
*/
|
|
authorizedToRestoreAnyResources() {
|
|
return (
|
|
this.resources.length > 0 &&
|
|
Boolean(
|
|
_.find(this.resources, resource => resource.authorizedToRestore)
|
|
)
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Determine whether the user is authorized to perform actions on the delete menu
|
|
*/
|
|
canShowDeleteMenu() {
|
|
return (
|
|
this.hasId &&
|
|
Boolean(
|
|
this.authorizedToDeleteSelectedResources ||
|
|
this.authorizedToForceDeleteSelectedResources ||
|
|
this.authorizedToDeleteAnyResources ||
|
|
this.authorizedToForceDeleteAnyResources ||
|
|
this.authorizedToRestoreSelectedResources ||
|
|
this.authorizedToRestoreAnyResources
|
|
)
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Return the currently encoded filter string from the store
|
|
*/
|
|
encodedFilters() {
|
|
return this.$store.getters[`${this.resourceName}/currentEncodedFilters`]
|
|
},
|
|
|
|
/**
|
|
* Return the initial encoded filters from the query string
|
|
*/
|
|
initialEncodedFilters() {
|
|
return this.$route.query[this.filterParameter] || ''
|
|
},
|
|
|
|
paginationComponent() {
|
|
return `pagination-${Nova.config['pagination'] || 'links'}`
|
|
},
|
|
|
|
hasNextPage() {
|
|
return Boolean(
|
|
this.resourceResponse && this.resourceResponse.next_page_url
|
|
)
|
|
},
|
|
|
|
hasPreviousPage() {
|
|
return Boolean(
|
|
this.resourceResponse && this.resourceResponse.prev_page_url
|
|
)
|
|
},
|
|
|
|
totalPages() {
|
|
return Math.ceil(this.allMatchingResourceCount / this.currentPerPage)
|
|
},
|
|
|
|
/**
|
|
* Return the resource count label
|
|
*/
|
|
resourceCountLabel() {
|
|
const first = this.perPage * (this.currentPage - 1)
|
|
|
|
return (
|
|
this.resources.length &&
|
|
`${first + 1}-${first + this.resources.length} ${this.__('of')} ${
|
|
this.allMatchingResourceCount
|
|
}`
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Get the current per page value from the query string.
|
|
*/
|
|
currentPerPage() {
|
|
return this.perPage
|
|
},
|
|
|
|
/**
|
|
* The per-page options configured for this resource.
|
|
*/
|
|
perPageOptions() {
|
|
if (this.resourceResponse) {
|
|
return this.resourceResponse.per_page_options
|
|
}
|
|
},
|
|
|
|
/**
|
|
* The Lense name.
|
|
*/
|
|
lenseName() {
|
|
if (this.resourceResponse) {
|
|
return this.resourceResponse.name
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|