Последняя версия с сервера прошлого разработчика
This commit is contained in:
15
nova/resources/js/components/Index/BadgeField.vue
Executable file
15
nova/resources/js/components/Index/BadgeField.vue
Executable file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div><badge :label="field.label" :extra-classes="field.typeClass" /></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Badge from '../Badge'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Badge,
|
||||
},
|
||||
|
||||
props: ['resourceName', 'viaResource', 'viaResourceId', 'field'],
|
||||
}
|
||||
</script>
|
||||
28
nova/resources/js/components/Index/BelongsToField.vue
Executable file
28
nova/resources/js/components/Index/BelongsToField.vue
Executable file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<span>
|
||||
<span v-if="field.viewable && field.value">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'detail',
|
||||
params: {
|
||||
resourceName: field.resourceName,
|
||||
resourceId: field.belongsToId,
|
||||
},
|
||||
}"
|
||||
class="no-underline dim text-primary font-bold"
|
||||
>
|
||||
{{ field.value }}
|
||||
</router-link>
|
||||
</span>
|
||||
<span v-else-if="field.value">{{ field.value }}</span>
|
||||
<span v-else>—</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
}
|
||||
</script>
|
||||
11
nova/resources/js/components/Index/BooleanField.vue
Executable file
11
nova/resources/js/components/Index/BooleanField.vue
Executable file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<boolean-icon :value="field.value" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
}
|
||||
</script>
|
||||
61
nova/resources/js/components/Index/BooleanGroupField.vue
Executable file
61
nova/resources/js/components/Index/BooleanGroupField.vue
Executable file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<tooltip trigger="click">
|
||||
<div class="text-primary inline-flex items-center dim cursor-pointer">
|
||||
<span class="text-primary font-bold">{{ __('View') }}</span>
|
||||
</div>
|
||||
|
||||
<tooltip-content slot="content">
|
||||
<ul class="list-reset" v-if="value.length > 0">
|
||||
<li v-for="option in value" class="mb-1">
|
||||
<span
|
||||
:class="classes[option.checked]"
|
||||
class="inline-flex items-center py-1 pl-2 pr-3 rounded-full font-bold text-sm leading-tight"
|
||||
>
|
||||
<boolean-icon :value="option.checked" width="20" height="20" />
|
||||
<span class="ml-1">{{ option.label }}</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<span v-else>{{ this.field.noValueText }}</span>
|
||||
</tooltip-content>
|
||||
</tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
|
||||
data: () => ({
|
||||
value: [],
|
||||
classes: {
|
||||
true: 'bg-success-light text-success-dark',
|
||||
false: 'bg-danger-light text-danger-dark',
|
||||
},
|
||||
}),
|
||||
|
||||
created() {
|
||||
this.field.value = this.field.value || {}
|
||||
|
||||
this.value = _(this.field.options)
|
||||
.map(o => {
|
||||
return {
|
||||
name: o.name,
|
||||
label: o.label,
|
||||
checked: this.field.value[o.name] || false,
|
||||
}
|
||||
})
|
||||
.filter(o => {
|
||||
if (this.field.hideFalseValues === true && o.checked === false) {
|
||||
return false
|
||||
} else if (this.field.hideTrueValues === true && o.checked === true) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
.value()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
24
nova/resources/js/components/Index/Checkbox.vue
Executable file
24
nova/resources/js/components/Index/Checkbox.vue
Executable file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
:disabled="disabled"
|
||||
:checked="checked"
|
||||
@change="$emit('input', $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
checked: {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
24
nova/resources/js/components/Index/CurrencyField.vue
Executable file
24
nova/resources/js/components/Index/CurrencyField.vue
Executable file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="hasValue">
|
||||
<div v-if="field.asHtml" v-html="field.value"></div>
|
||||
<span v-else>{{ field.value }}</span>
|
||||
</template>
|
||||
<p v-else>—</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Determine if the field has a value other than null.
|
||||
*/
|
||||
hasValue() {
|
||||
return this.field.value !== null
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
24
nova/resources/js/components/Index/DateField.vue
Executable file
24
nova/resources/js/components/Index/DateField.vue
Executable file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<span v-if="field.value" class="whitespace-no-wrap">{{
|
||||
formattedDate
|
||||
}}</span>
|
||||
<span v-else>—</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
|
||||
computed: {
|
||||
formattedDate() {
|
||||
if (this.field.format) {
|
||||
return moment(this.field.value).format(this.field.format)
|
||||
}
|
||||
|
||||
return this.field.value
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
27
nova/resources/js/components/Index/DateTimeField.vue
Executable file
27
nova/resources/js/components/Index/DateTimeField.vue
Executable file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<span v-if="field.value" class="whitespace-no-wrap">{{
|
||||
localizedDateTime
|
||||
}}</span>
|
||||
<span v-else class="whitespace-no-wrap">—</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { InteractsWithDates } from 'laravel-nova'
|
||||
|
||||
export default {
|
||||
mixins: [InteractsWithDates],
|
||||
|
||||
props: ['resourceName', 'field'],
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Get the localized date time.
|
||||
*/
|
||||
localizedDateTime() {
|
||||
return this.localizeDateTimeField(this.field)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
13
nova/resources/js/components/Index/FakeCheckbox.vue
Executable file
13
nova/resources/js/components/Index/FakeCheckbox.vue
Executable file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<checkbox :checked="checked" :disabled="true" class="pointer-events-none" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
checked: {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
27
nova/resources/js/components/Index/FileField.vue
Executable file
27
nova/resources/js/components/Index/FileField.vue
Executable file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<p>
|
||||
<img
|
||||
v-if="imageUrl"
|
||||
:src="imageUrl"
|
||||
style="object-fit: cover"
|
||||
class="align-bottom w-8 h-8"
|
||||
:class="{ 'rounded-full': field.rounded, rounded: !field.rounded }"
|
||||
/>
|
||||
<span v-else>—</span>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['viaResource', 'viaResourceId', 'resourceName', 'field'],
|
||||
computed: {
|
||||
imageUrl() {
|
||||
if (this.field.previewUrl && !this.field.thumbnailUrl) {
|
||||
return this.field.previewUrl
|
||||
}
|
||||
|
||||
return this.field.thumbnailUrl
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
9
nova/resources/js/components/Index/HeadingField.vue
Executable file
9
nova/resources/js/components/Index/HeadingField.vue
Executable file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<span />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['field', 'viaResource', 'viaResourceId', 'resourceName'],
|
||||
}
|
||||
</script>
|
||||
9
nova/resources/js/components/Index/HiddenField.vue
Executable file
9
nova/resources/js/components/Index/HiddenField.vue
Executable file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div class="hidden" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
}
|
||||
</script>
|
||||
33
nova/resources/js/components/Index/IdField.vue
Executable file
33
nova/resources/js/components/Index/IdField.vue
Executable file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<router-link
|
||||
v-if="hasValue"
|
||||
:to="{
|
||||
name: 'detail',
|
||||
params: {
|
||||
resourceName: resourceName,
|
||||
resourceId: field.value,
|
||||
},
|
||||
}"
|
||||
class="no-underline dim text-primary font-bold"
|
||||
>
|
||||
{{ field.pivotValue || field.value }}
|
||||
</router-link>
|
||||
<p v-else>—</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Determine if the field has a value other than null.
|
||||
*/
|
||||
hasValue() {
|
||||
return this.field.value !== null
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
95
nova/resources/js/components/Index/InlineActionSelector.vue
Executable file
95
nova/resources/js/components/Index/InlineActionSelector.vue
Executable file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<span>
|
||||
<select
|
||||
ref="selectBox"
|
||||
v-if="actions.length > 1"
|
||||
class="rounded-sm select-box-sm mr-2 h-6 text-xs appearance-none bg-40 pl-2 pr-6 active:outline-none active:shadow-outline focus:outline-none focus:shadow-outline"
|
||||
style="max-width: 90px"
|
||||
@change="handleSelectionChange"
|
||||
dusk="inline-action-select"
|
||||
>
|
||||
<option disabled selected>{{ __('Actions') }}</option>
|
||||
<option
|
||||
v-for="action in actions"
|
||||
:key="action.uriKey"
|
||||
:value="action.uriKey"
|
||||
>
|
||||
{{ action.name }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<button
|
||||
v-else
|
||||
v-for="action in actions"
|
||||
:key="action.uriKey"
|
||||
@click="executeSingleAction(action)"
|
||||
class="btn btn-xs mr-1"
|
||||
:class="action.class"
|
||||
dusk="run-inline-action-button"
|
||||
:data-testid="action.uriKey"
|
||||
>
|
||||
{{ action.name }}
|
||||
</button>
|
||||
|
||||
<!-- Action Confirmation Modal -->
|
||||
<portal to="modals">
|
||||
<component
|
||||
v-if="confirmActionModalOpened"
|
||||
class="text-left"
|
||||
:is="selectedAction.component"
|
||||
:working="working"
|
||||
:selected-resources="selectedResources"
|
||||
:resource-name="resourceName"
|
||||
:action="selectedAction"
|
||||
:endpoint="actionsEndpoint"
|
||||
:errors="errors"
|
||||
@confirm="executeAction"
|
||||
@close="closeConfirmationModal"
|
||||
/>
|
||||
|
||||
<component
|
||||
:is="actionResponseData.modal"
|
||||
@close="closeActionResponseModal"
|
||||
v-if="showActionResponseModal"
|
||||
:data="actionResponseData"
|
||||
/>
|
||||
</portal>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HandlesActions from '@/mixins/HandlesActions'
|
||||
|
||||
export default {
|
||||
mixins: [HandlesActions],
|
||||
|
||||
props: {
|
||||
resource: {},
|
||||
actions: {},
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
showActionResponseModal: false,
|
||||
actionResponseData: {},
|
||||
}),
|
||||
|
||||
methods: {
|
||||
handleSelectionChange(event) {
|
||||
this.selectedActionKey = event.target.value
|
||||
this.determineActionStrategy()
|
||||
this.$refs.selectBox.selectedIndex = 0
|
||||
},
|
||||
|
||||
executeSingleAction(action) {
|
||||
this.selectedActionKey = action.uriKey
|
||||
this.determineActionStrategy()
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
selectedResources() {
|
||||
return [this.resource.id.value]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
26
nova/resources/js/components/Index/LineField.vue
Executable file
26
nova/resources/js/components/Index/LineField.vue
Executable file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<template v-if="hasValue">
|
||||
<div v-if="field.asHtml" v-html="field.value"></div>
|
||||
<span v-else class="whitespace-no-wrap" :class="field.classes">{{
|
||||
field.value
|
||||
}}</span>
|
||||
</template>
|
||||
<p v-else>—</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Determine if the field has a value other than null.
|
||||
*/
|
||||
hasValue() {
|
||||
return this.field.value !== null
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
38
nova/resources/js/components/Index/MorphToActionTargetField.vue
Executable file
38
nova/resources/js/components/Index/MorphToActionTargetField.vue
Executable file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<router-link
|
||||
v-if="field.viewable && field.value && !isResourceBeingViewed"
|
||||
:to="{
|
||||
name: 'detail',
|
||||
params: {
|
||||
resourceName: field.resourceName,
|
||||
resourceId: field.morphToId,
|
||||
},
|
||||
}"
|
||||
class="dim no-underline text-primary font-bold"
|
||||
:class="`text-${field.textAlign}`"
|
||||
>
|
||||
{{ field.resourceLabel }}: {{ field.value }}
|
||||
</router-link>
|
||||
<span v-else-if="field.value">
|
||||
{{ field.resourceLabel || field.morphToType }}: {{ field.value }}
|
||||
</span>
|
||||
<span v-else> - </span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'viaResource', 'viaResourceId', 'field'],
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Determine if the resource being viewed matches the field's value.
|
||||
*/
|
||||
isResourceBeingViewed() {
|
||||
return (
|
||||
this.field.morphToType == this.viaResource &&
|
||||
this.field.morphToId == this.viaResourceId
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
30
nova/resources/js/components/Index/MorphToField.vue
Executable file
30
nova/resources/js/components/Index/MorphToField.vue
Executable file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<span>
|
||||
<span v-if="field.viewable && field.value">
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'detail',
|
||||
params: {
|
||||
resourceName: field.resourceName,
|
||||
resourceId: field.morphToId,
|
||||
},
|
||||
}"
|
||||
class="no-underline dim text-primary font-bold"
|
||||
>
|
||||
{{ field.resourceLabel }}: {{ field.value }}
|
||||
</router-link>
|
||||
</span>
|
||||
<span v-else-if="field.value">
|
||||
{{ field.resourceLabel || field.morphToType }}: {{ field.value }}
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
}
|
||||
</script>
|
||||
13
nova/resources/js/components/Index/PasswordField.vue
Executable file
13
nova/resources/js/components/Index/PasswordField.vue
Executable file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<span class="font-bold">
|
||||
· · · · · · · ·
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
}
|
||||
</script>
|
||||
250
nova/resources/js/components/Index/ResourceTableRow.vue
Executable file
250
nova/resources/js/components/Index/ResourceTableRow.vue
Executable file
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<tr
|
||||
:dusk="resource['id'].value + '-row'"
|
||||
:data-pivot-id="resource['id'].pivotValue"
|
||||
>
|
||||
<!-- Resource Selection Checkbox -->
|
||||
<td class="w-16" v-if="shouldShowCheckboxes">
|
||||
<checkbox
|
||||
:data-testid="`${testId}-checkbox`"
|
||||
:dusk="`${resource['id'].value}-checkbox`"
|
||||
v-if="shouldShowCheckboxes"
|
||||
:checked="checked"
|
||||
@input="toggleSelection"
|
||||
/>
|
||||
</td>
|
||||
|
||||
<!-- Fields -->
|
||||
<td v-for="field in resource.fields">
|
||||
<component
|
||||
:is="'index-' + field.component"
|
||||
:class="`text-${field.textAlign}`"
|
||||
:resource-name="resourceName"
|
||||
:via-resource="viaResource"
|
||||
:via-resource-id="viaResourceId"
|
||||
:field="field"
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td class="td-fit text-right pr-6 align-middle">
|
||||
<div class="inline-flex items-center">
|
||||
<!-- Actions Menu -->
|
||||
<inline-action-selector
|
||||
v-if="availableActions.length > 0"
|
||||
class="mr-3"
|
||||
:resource="resource"
|
||||
:resource-name="resourceName"
|
||||
:actions="availableActions"
|
||||
:endpoint="actionsEndpoint"
|
||||
@actionExecuted="$emit('actionExecuted')"
|
||||
/>
|
||||
|
||||
<!-- View Resource Link -->
|
||||
<span v-if="resource.authorizedToView" class="inline-flex">
|
||||
<router-link
|
||||
:data-testid="`${testId}-view-button`"
|
||||
:dusk="`${resource['id'].value}-view-button`"
|
||||
class="cursor-pointer text-70 hover:text-primary mr-3 inline-flex items-center"
|
||||
v-tooltip.click="__('View')"
|
||||
:to="{
|
||||
name: 'detail',
|
||||
params: {
|
||||
resourceName: resourceName,
|
||||
resourceId: resource['id'].value,
|
||||
},
|
||||
}"
|
||||
>
|
||||
<icon type="view" width="22" height="18" view-box="0 0 22 16" />
|
||||
</router-link>
|
||||
</span>
|
||||
|
||||
<span v-if="resource.authorizedToUpdate" class="inline-flex">
|
||||
<!-- Edit Pivot Button -->
|
||||
<router-link
|
||||
v-if="
|
||||
relationshipType == 'belongsToMany' ||
|
||||
relationshipType == 'morphToMany'
|
||||
"
|
||||
class="inline-flex cursor-pointer text-70 hover:text-primary mr-3"
|
||||
:dusk="`${resource['id'].value}-edit-attached-button`"
|
||||
v-tooltip.click="__('Edit Attached')"
|
||||
:to="{
|
||||
name: 'edit-attached',
|
||||
params: {
|
||||
resourceName: viaResource,
|
||||
resourceId: viaResourceId,
|
||||
relatedResourceName: resourceName,
|
||||
relatedResourceId: resource['id'].value,
|
||||
},
|
||||
query: {
|
||||
viaRelationship: viaRelationship,
|
||||
viaPivotId: resource['id'].pivotValue,
|
||||
},
|
||||
}"
|
||||
>
|
||||
<icon type="edit" />
|
||||
</router-link>
|
||||
|
||||
<!-- Edit Resource Link -->
|
||||
<router-link
|
||||
v-else
|
||||
class="inline-flex cursor-pointer text-70 hover:text-primary mr-3"
|
||||
:dusk="`${resource['id'].value}-edit-button`"
|
||||
:to="{
|
||||
name: 'edit',
|
||||
params: {
|
||||
resourceName: resourceName,
|
||||
resourceId: resource['id'].value,
|
||||
},
|
||||
query: {
|
||||
viaResource: viaResource,
|
||||
viaResourceId: viaResourceId,
|
||||
viaRelationship: viaRelationship,
|
||||
},
|
||||
}"
|
||||
v-tooltip.click="__('Edit')"
|
||||
>
|
||||
<icon type="edit" />
|
||||
</router-link>
|
||||
</span>
|
||||
|
||||
<!-- Delete Resource Link -->
|
||||
<button
|
||||
:data-testid="`${testId}-delete-button`"
|
||||
:dusk="`${resource['id'].value}-delete-button`"
|
||||
class="inline-flex appearance-none cursor-pointer text-70 hover:text-primary mr-3"
|
||||
v-tooltip.click="__(viaManyToMany ? 'Detach' : 'Delete')"
|
||||
v-if="
|
||||
resource.authorizedToDelete &&
|
||||
(!resource.softDeleted || viaManyToMany)
|
||||
"
|
||||
@click.prevent="openDeleteModal"
|
||||
>
|
||||
<icon />
|
||||
</button>
|
||||
|
||||
<!-- Restore Resource Link -->
|
||||
<button
|
||||
:dusk="`${resource['id'].value}-restore-button`"
|
||||
class="appearance-none cursor-pointer text-70 hover:text-primary mr-3"
|
||||
v-if="
|
||||
resource.authorizedToRestore &&
|
||||
resource.softDeleted &&
|
||||
!viaManyToMany
|
||||
"
|
||||
v-tooltip.click="__('Restore')"
|
||||
@click.prevent="openRestoreModal"
|
||||
>
|
||||
<icon type="restore" with="20" height="21" />
|
||||
</button>
|
||||
|
||||
<portal
|
||||
to="modals"
|
||||
transition="fade-transition"
|
||||
v-if="deleteModalOpen || restoreModalOpen"
|
||||
>
|
||||
<delete-resource-modal
|
||||
v-if="deleteModalOpen"
|
||||
@confirm="confirmDelete"
|
||||
@close="closeDeleteModal"
|
||||
:mode="viaManyToMany ? 'detach' : 'delete'"
|
||||
>
|
||||
<div slot-scope="{ uppercaseMode, mode }" class="p-8">
|
||||
<heading :level="2" class="mb-6">{{
|
||||
__(uppercaseMode + ' Resource')
|
||||
}}</heading>
|
||||
<p class="text-80 leading-normal">
|
||||
{{ __('Are you sure you want to ' + mode + ' this resource?') }}
|
||||
</p>
|
||||
</div>
|
||||
</delete-resource-modal>
|
||||
|
||||
<restore-resource-modal
|
||||
v-if="restoreModalOpen"
|
||||
@confirm="confirmRestore"
|
||||
@close="closeRestoreModal"
|
||||
>
|
||||
<div class="p-8">
|
||||
<heading :level="2" class="mb-6">{{
|
||||
__('Restore Resource')
|
||||
}}</heading>
|
||||
<p class="text-80 leading-normal">
|
||||
{{ __('Are you sure you want to restore this resource?') }}
|
||||
</p>
|
||||
</div>
|
||||
</restore-resource-modal>
|
||||
</portal>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: [
|
||||
'testId',
|
||||
'deleteResource',
|
||||
'restoreResource',
|
||||
'resource',
|
||||
'resourcesSelected',
|
||||
'resourceName',
|
||||
'relationshipType',
|
||||
'viaRelationship',
|
||||
'viaResource',
|
||||
'viaResourceId',
|
||||
'viaManyToMany',
|
||||
'checked',
|
||||
'actionsAreAvailable',
|
||||
'actionsEndpoint',
|
||||
'shouldShowCheckboxes',
|
||||
'updateSelectionStatus',
|
||||
'queryString',
|
||||
],
|
||||
|
||||
data: () => ({
|
||||
deleteModalOpen: false,
|
||||
restoreModalOpen: false,
|
||||
}),
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Select the resource in the parent component
|
||||
*/
|
||||
toggleSelection() {
|
||||
this.updateSelectionStatus(this.resource)
|
||||
},
|
||||
|
||||
openDeleteModal() {
|
||||
this.deleteModalOpen = true
|
||||
},
|
||||
|
||||
confirmDelete() {
|
||||
this.deleteResource(this.resource)
|
||||
this.closeDeleteModal()
|
||||
},
|
||||
|
||||
closeDeleteModal() {
|
||||
this.deleteModalOpen = false
|
||||
},
|
||||
|
||||
openRestoreModal() {
|
||||
this.restoreModalOpen = true
|
||||
},
|
||||
|
||||
confirmRestore() {
|
||||
this.restoreResource(this.resource)
|
||||
this.closeRestoreModal()
|
||||
},
|
||||
|
||||
closeRestoreModal() {
|
||||
this.restoreModalOpen = false
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
availableActions() {
|
||||
return _.filter(this.resource.actions, a => a.showOnTableRow)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
131
nova/resources/js/components/Index/SortableIcon.vue
Executable file
131
nova/resources/js/components/Index/SortableIcon.vue
Executable file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<span
|
||||
@click.prevent="handleClick"
|
||||
class="cursor-pointer inline-flex items-center"
|
||||
:dusk="'sort-' + uriKey"
|
||||
>
|
||||
<slot />
|
||||
|
||||
<svg
|
||||
class="ml-2 flex-no-shrink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="8"
|
||||
height="14"
|
||||
viewBox="0 0 8 14"
|
||||
>
|
||||
<path
|
||||
:class="descClass"
|
||||
d="M1.70710678 4.70710678c-.39052429.39052429-1.02368927.39052429-1.41421356 0-.3905243-.39052429-.3905243-1.02368927 0-1.41421356l3-3c.39052429-.3905243 1.02368927-.3905243 1.41421356 0l3 3c.39052429.39052429.39052429 1.02368927 0 1.41421356-.39052429.39052429-1.02368927.39052429-1.41421356 0L4 2.41421356 1.70710678 4.70710678z"
|
||||
/>
|
||||
<path
|
||||
:class="ascClass"
|
||||
d="M6.29289322 9.29289322c.39052429-.39052429 1.02368927-.39052429 1.41421356 0 .39052429.39052429.39052429 1.02368928 0 1.41421358l-3 3c-.39052429.3905243-1.02368927.3905243-1.41421356 0l-3-3c-.3905243-.3905243-.3905243-1.02368929 0-1.41421358.3905243-.39052429 1.02368927-.39052429 1.41421356 0L4 11.5857864l2.29289322-2.29289318z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
resourceName: String,
|
||||
uriKey: String,
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Handle the clicke event.
|
||||
*/
|
||||
handleClick() {
|
||||
if (this.isSorted && this.isDescDirection) {
|
||||
this.$emit('reset')
|
||||
} else {
|
||||
this.$emit('sort', {
|
||||
key: this.uriKey,
|
||||
direction: this.direction,
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Determine if the sorting direction is descending.
|
||||
*/
|
||||
isDescDirection() {
|
||||
return this.direction == 'desc'
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if the sorting direction is ascending.
|
||||
*/
|
||||
isAscDirection() {
|
||||
return this.direction == 'asc'
|
||||
},
|
||||
|
||||
/**
|
||||
* The CSS class to apply to the ascending arrow icon
|
||||
*/
|
||||
ascClass() {
|
||||
if (this.isSorted && this.isDescDirection) {
|
||||
return 'fill-80'
|
||||
}
|
||||
|
||||
return 'fill-60'
|
||||
},
|
||||
|
||||
/**
|
||||
* The CSS class to apply to the descending arrow icon
|
||||
*/
|
||||
descClass() {
|
||||
if (this.isSorted && this.isAscDirection) {
|
||||
return 'fill-80'
|
||||
}
|
||||
|
||||
return 'fill-60'
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether this column is being sorted
|
||||
*/
|
||||
isSorted() {
|
||||
return (
|
||||
this.sortColumn == this.uriKey &&
|
||||
['asc', 'desc'].includes(this.direction)
|
||||
)
|
||||
},
|
||||
|
||||
/**
|
||||
* The current order query parameter for this resource
|
||||
*/
|
||||
sortKey() {
|
||||
return `${this.resourceName}_order`
|
||||
},
|
||||
|
||||
/**
|
||||
* The current order query parameter value
|
||||
*/
|
||||
sortColumn() {
|
||||
return this.$route.query[this.sortKey]
|
||||
},
|
||||
|
||||
/**
|
||||
* The current direction query parameter for this resource
|
||||
*/
|
||||
directionKey() {
|
||||
return `${this.resourceName}_direction`
|
||||
},
|
||||
|
||||
/**
|
||||
* The current direction query parameter value
|
||||
*/
|
||||
direction() {
|
||||
return this.$route.query[this.directionKey]
|
||||
},
|
||||
|
||||
notSorted() {
|
||||
return !!!this.isSorted
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
88
nova/resources/js/components/Index/SparklineField.vue
Executable file
88
nova/resources/js/components/Index/SparklineField.vue
Executable file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div v-if="hasData">
|
||||
<div
|
||||
ref="chart"
|
||||
class="ct-chart"
|
||||
:style="{ width: chartWidth, height: chartHeight }"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Chartist from 'chartist'
|
||||
import 'chartist/dist/chartist.min.css'
|
||||
|
||||
// Default chart diameters.
|
||||
const defaultHeight = 50
|
||||
const defaultWidth = 100
|
||||
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
|
||||
data: () => ({ chartist: null }),
|
||||
|
||||
watch: {
|
||||
'field.data': function (newData, oldData) {
|
||||
this.renderChart()
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
renderChart() {
|
||||
this.chartist.update(this.field.data)
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.chartist = new Chartist[this.chartStyle](
|
||||
this.$refs.chart,
|
||||
{ series: [this.field.data] },
|
||||
{
|
||||
height: this.chartHeight,
|
||||
width: this.chartWidth,
|
||||
showPoint: false,
|
||||
fullWidth: true,
|
||||
chartPadding: { top: 0, right: 0, bottom: 0, left: 0 },
|
||||
axisX: { showGrid: false, showLabel: false, offset: 0 },
|
||||
axisY: { showGrid: false, showLabel: false, offset: 0 },
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Determine if the field has a value other than null.
|
||||
*/
|
||||
hasData() {
|
||||
return this.field.data.length > 0
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine the chart style.
|
||||
*/
|
||||
chartStyle() {
|
||||
const validTypes = ['line', 'bar']
|
||||
let chartStyle = this.field.chartStyle.toLowerCase()
|
||||
|
||||
// Line and Bar are the only valid types.
|
||||
if (!validTypes.includes(chartStyle)) return 'Line'
|
||||
|
||||
return chartStyle.charAt(0).toUpperCase() + chartStyle.slice(1)
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine the chart height.
|
||||
*/
|
||||
chartHeight() {
|
||||
return this.field.height || defaultHeight
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine the chart width.
|
||||
*/
|
||||
chartWidth() {
|
||||
return this.field.width || defaultWidth
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
32
nova/resources/js/components/Index/StackField.vue
Executable file
32
nova/resources/js/components/Index/StackField.vue
Executable file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<template v-if="hasValue">
|
||||
<div class="leading-normal my-2">
|
||||
<component
|
||||
:key="line.value"
|
||||
v-for="line in field.lines"
|
||||
class="whitespace-no-wrap"
|
||||
:is="`index-${line.component}`"
|
||||
:field="line"
|
||||
:resourceName="resourceName"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<p v-else>—</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Determine if the field has a value other than null.
|
||||
*/
|
||||
hasValue() {
|
||||
return this.field.lines
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
17
nova/resources/js/components/Index/StatusField.vue
Executable file
17
nova/resources/js/components/Index/StatusField.vue
Executable file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="flex items-center">
|
||||
<span class="mr-3 text-60" v-if="field.loadingWords.includes(field.value)">
|
||||
<loader width="30" />
|
||||
</span>
|
||||
|
||||
<span :class="{ 'text-danger': field.failedWords.includes(field.value) }">
|
||||
{{ field.value }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
}
|
||||
</script>
|
||||
24
nova/resources/js/components/Index/TextField.vue
Executable file
24
nova/resources/js/components/Index/TextField.vue
Executable file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div :class="`text-${field.textAlign}`">
|
||||
<template v-if="hasValue">
|
||||
<div v-if="field.asHtml" v-html="field.value"></div>
|
||||
<span v-else class="whitespace-no-wrap">{{ field.value }}</span>
|
||||
</template>
|
||||
<p v-else>—</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['resourceName', 'field'],
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Determine if the field has a value other than null.
|
||||
*/
|
||||
hasValue() {
|
||||
return this.field.value !== null
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user