add nova
This commit is contained in:
188
nova/resources/js/components/Modals/ConfirmActionModal.vue
Normal file
188
nova/resources/js/components/Modals/ConfirmActionModal.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<Modal
|
||||
:show="show"
|
||||
@close-via-escape="handlePreventModalAbandonmentOnClose"
|
||||
role="dialog"
|
||||
:size="action.modalSize"
|
||||
:modal-style="action.modalStyle"
|
||||
:use-focus-trap="usesFocusTrap"
|
||||
>
|
||||
<form
|
||||
ref="theForm"
|
||||
autocomplete="off"
|
||||
@change="onUpdateFormStatus"
|
||||
@submit.prevent.stop="$emit('confirm')"
|
||||
:data-form-unique-id="formUniqueId"
|
||||
class="bg-white dark:bg-gray-800"
|
||||
:class="{
|
||||
'rounded-lg shadow-lg overflow-hidden space-y-6':
|
||||
action.modalStyle === 'window',
|
||||
'flex flex-col justify-between h-full':
|
||||
action.modalStyle === 'fullscreen',
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="space-y-6"
|
||||
:class="{
|
||||
'overflow-hidden overflow-y-auto': action.modalStyle === 'fullscreen',
|
||||
}"
|
||||
>
|
||||
<ModalHeader v-text="action.name" />
|
||||
|
||||
<!-- Confirmation Text -->
|
||||
<p
|
||||
v-if="action.confirmText"
|
||||
class="px-8"
|
||||
:class="{ 'text-red-500': action.destructive }"
|
||||
>
|
||||
{{ action.confirmText }}
|
||||
</p>
|
||||
|
||||
<!-- Action Fields -->
|
||||
<div v-if="action.fields.length > 0">
|
||||
<div
|
||||
class="action"
|
||||
v-for="field in action.fields"
|
||||
:key="field.attribute"
|
||||
>
|
||||
<component
|
||||
:is="'form-' + field.component"
|
||||
:errors="errors"
|
||||
:resource-name="resourceName"
|
||||
:field="field"
|
||||
:show-help-text="true"
|
||||
:form-unique-id="formUniqueId"
|
||||
:mode="
|
||||
action.modalStyle === 'fullscreen'
|
||||
? 'action-fullscreen'
|
||||
: 'action-modal'
|
||||
"
|
||||
:sync-endpoint="syncEndpoint"
|
||||
@field-changed="onUpdateFieldStatus"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ModalFooter>
|
||||
<div class="flex items-center ml-auto">
|
||||
<CancelButton
|
||||
component="button"
|
||||
type="button"
|
||||
dusk="cancel-action-button"
|
||||
class="ml-auto mr-3"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
{{ action.cancelButtonText }}
|
||||
</CancelButton>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
ref="runButton"
|
||||
dusk="confirm-action-button"
|
||||
:loading="working"
|
||||
variant="solid"
|
||||
:state="action.destructive ? 'danger' : 'default'"
|
||||
>
|
||||
{{ action.confirmButtonText }}
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PreventsModalAbandonment } from '@/mixins'
|
||||
import isObject from 'lodash/isObject'
|
||||
import { uid } from 'uid/single'
|
||||
import { Button } from 'laravel-nova-ui'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Button,
|
||||
},
|
||||
|
||||
emits: ['confirm', 'close'],
|
||||
|
||||
mixins: [PreventsModalAbandonment],
|
||||
|
||||
props: {
|
||||
action: { type: Object, required: true },
|
||||
endpoint: { type: String, required: false },
|
||||
errors: { type: Object, required: true },
|
||||
resourceName: { type: String, required: true },
|
||||
selectedResources: { type: [Array, String], required: true },
|
||||
show: { type: Boolean, default: false },
|
||||
working: Boolean,
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
loading: true,
|
||||
formUniqueId: uid(),
|
||||
}),
|
||||
|
||||
created() {
|
||||
document.addEventListener('keydown', this.handleKeydown)
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
document.removeEventListener('keydown', this.handleKeydown)
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Prevent accidental abandonment only if form was changed.
|
||||
*/
|
||||
onUpdateFormStatus() {
|
||||
this.updateModalStatus()
|
||||
},
|
||||
|
||||
onUpdateFieldStatus() {
|
||||
this.onUpdateFormStatus()
|
||||
},
|
||||
|
||||
handlePreventModalAbandonmentOnClose(event) {
|
||||
this.handlePreventModalAbandonment(
|
||||
() => {
|
||||
this.$emit('close')
|
||||
},
|
||||
() => {
|
||||
event.stopPropagation()
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
syncEndpoint() {
|
||||
let searchParams = new URLSearchParams({ action: this.action.uriKey })
|
||||
|
||||
if (this.selectedResources === 'all') {
|
||||
searchParams.append('resources', 'all')
|
||||
} else {
|
||||
this.selectedResources.forEach(resource => {
|
||||
searchParams.append(
|
||||
'resources[]',
|
||||
isObject(resource) ? resource.id.value : resource
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
(this.endpoint || `/nova-api/${this.resourceName}/action`) +
|
||||
'?' +
|
||||
searchParams.toString()
|
||||
)
|
||||
},
|
||||
|
||||
usesFocusTrap() {
|
||||
return this.loading === false && this.action.fields.length > 0
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<Modal :show="show" role="alertdialog" size="md">
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden"
|
||||
style="width: 460px"
|
||||
>
|
||||
<ModalHeader v-text="__('Delete File')" />
|
||||
<ModalContent>
|
||||
<p class="leading-tight">
|
||||
{{ __('Are you sure you want to delete this file?') }}
|
||||
</p>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<div class="ml-auto">
|
||||
<LinkButton
|
||||
dusk="cancel-upload-delete-button"
|
||||
type="button"
|
||||
@click.prevent="handleClose"
|
||||
class="mr-3"
|
||||
>
|
||||
{{ __('Cancel') }}
|
||||
</LinkButton>
|
||||
|
||||
<Button
|
||||
@click.prevent="handleConfirm"
|
||||
ref="confirmButton"
|
||||
dusk="confirm-upload-delete-button"
|
||||
:loading="working"
|
||||
state="danger"
|
||||
:label="__('Delete')"
|
||||
/>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Button } from 'laravel-nova-ui'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Button,
|
||||
},
|
||||
|
||||
emits: ['confirm', 'close'],
|
||||
|
||||
props: {
|
||||
show: { type: Boolean, default: false },
|
||||
},
|
||||
|
||||
data: () => ({ working: false }),
|
||||
|
||||
watch: {
|
||||
show(showing) {
|
||||
if (showing === false) {
|
||||
this.working = false
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.working = false
|
||||
this.$emit('close')
|
||||
},
|
||||
|
||||
handleConfirm() {
|
||||
this.working = true
|
||||
this.$emit('confirm')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
75
nova/resources/js/components/Modals/CreateRelationModal.vue
Normal file
75
nova/resources/js/components/Modals/CreateRelationModal.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<Modal
|
||||
dusk="new-relation-modal"
|
||||
:show="show"
|
||||
@close-via-escape="handlePreventModalAbandonmentOnClose"
|
||||
:size="size"
|
||||
:use-focus-trap="!loading"
|
||||
>
|
||||
<div
|
||||
class="bg-gray-100 dark:bg-gray-700 rounded-lg shadow-lg overflow-hidden p-8"
|
||||
>
|
||||
<CreateResource
|
||||
:resource-name="resourceName"
|
||||
@create-cancelled="handleCreateCancelled"
|
||||
@finished-loading="() => (loading = false)"
|
||||
@refresh="handleRefresh"
|
||||
mode="modal"
|
||||
resource-id=""
|
||||
via-relationship=""
|
||||
via-resource-id=""
|
||||
via-resource=""
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PreventsModalAbandonment } from '@/mixins'
|
||||
import CreateResource from '@/views/Create'
|
||||
|
||||
export default {
|
||||
emits: ['set-resource', 'create-cancelled'],
|
||||
|
||||
mixins: [PreventsModalAbandonment],
|
||||
|
||||
components: {
|
||||
CreateResource,
|
||||
},
|
||||
|
||||
props: {
|
||||
show: { type: Boolean, default: false },
|
||||
size: { type: String, default: '2xl' },
|
||||
resourceName: {},
|
||||
resourceId: {},
|
||||
viaResource: {},
|
||||
viaResourceId: {},
|
||||
viaRelationship: {},
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
loading: true,
|
||||
}),
|
||||
|
||||
methods: {
|
||||
handleRefresh(data) {
|
||||
this.$emit('set-resource', data)
|
||||
},
|
||||
|
||||
handleCreateCancelled() {
|
||||
return this.$emit('create-cancelled')
|
||||
},
|
||||
|
||||
handlePreventModalAbandonmentOnClose(event) {
|
||||
this.handlePreventModalAbandonment(
|
||||
() => {
|
||||
this.handleCreateCancelled()
|
||||
},
|
||||
() => {
|
||||
event.stopPropagation()
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
98
nova/resources/js/components/Modals/DeleteResourceModal.vue
Normal file
98
nova/resources/js/components/Modals/DeleteResourceModal.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<Modal :show="show" role="alertdialog" size="sm">
|
||||
<form
|
||||
@submit.prevent="handleConfirm"
|
||||
class="mx-auto bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden"
|
||||
>
|
||||
<slot>
|
||||
<ModalHeader v-text="__(`${uppercaseMode} Resource`)" />
|
||||
<ModalContent>
|
||||
<p class="leading-normal">
|
||||
{{
|
||||
__(
|
||||
'Are you sure you want to ' + mode + ' the selected resources?'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</ModalContent>
|
||||
</slot>
|
||||
|
||||
<ModalFooter>
|
||||
<div class="ml-auto">
|
||||
<LinkButton
|
||||
type="button"
|
||||
dusk="cancel-delete-button"
|
||||
@click.prevent="handleClose"
|
||||
class="mr-3"
|
||||
>
|
||||
{{ __('Cancel') }}
|
||||
</LinkButton>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
ref="confirmButton"
|
||||
dusk="confirm-delete-button"
|
||||
:loading="working"
|
||||
state="danger"
|
||||
:label="__(uppercaseMode)"
|
||||
/>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import startCase from 'lodash/startCase'
|
||||
import { Button } from 'laravel-nova-ui'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Button,
|
||||
},
|
||||
|
||||
emits: ['confirm', 'close'],
|
||||
|
||||
props: {
|
||||
show: { type: Boolean, default: false },
|
||||
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'delete',
|
||||
validator: function (value) {
|
||||
return ['force delete', 'delete', 'detach'].indexOf(value) !== -1
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
working: false,
|
||||
}),
|
||||
|
||||
watch: {
|
||||
show(showing) {
|
||||
if (showing === false) {
|
||||
this.working = false
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit('close')
|
||||
this.working = false
|
||||
},
|
||||
|
||||
handleConfirm() {
|
||||
this.$emit('confirm')
|
||||
this.working = true
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
uppercaseMode() {
|
||||
return startCase(this.mode)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
189
nova/resources/js/components/Modals/Modal.vue
Normal file
189
nova/resources/js/components/Modals/Modal.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<teleport to="body">
|
||||
<template v-if="show">
|
||||
<div
|
||||
v-bind="defaultAttributes"
|
||||
class="modal fixed inset-0 z-[60]"
|
||||
:class="{
|
||||
'px-3 md:px-0 py-3 md:py-6 overflow-x-hidden overflow-y-auto':
|
||||
modalStyle === 'window',
|
||||
'h-full': modalStyle === 'fullscreen',
|
||||
}"
|
||||
:role="role"
|
||||
:data-modal-open="show"
|
||||
:aria-modal="show"
|
||||
>
|
||||
<div
|
||||
class="@container/modal relative mx-auto z-20"
|
||||
:class="contentClasses"
|
||||
ref="modalContent"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="fixed inset-0 z-[55] bg-gray-500/75 dark:bg-gray-900/75"
|
||||
dusk="modal-backdrop"
|
||||
/>
|
||||
</template>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useStore } from 'vuex'
|
||||
import filter from 'lodash/filter'
|
||||
import omit from 'lodash/omit'
|
||||
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
import {
|
||||
computed,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
useAttrs,
|
||||
watch,
|
||||
} from 'vue'
|
||||
|
||||
const modalContent = ref(null)
|
||||
|
||||
const attrs = useAttrs()
|
||||
|
||||
const emit = defineEmits(['showing', 'closing', 'close-via-escape'])
|
||||
|
||||
defineOptions({ inheritAttrs: false })
|
||||
|
||||
const props = defineProps({
|
||||
show: { type: Boolean, default: false },
|
||||
size: {
|
||||
type: String,
|
||||
default: 'xl',
|
||||
validator: v =>
|
||||
[
|
||||
'sm',
|
||||
'md',
|
||||
'lg',
|
||||
'xl',
|
||||
'2xl',
|
||||
'3xl',
|
||||
'4xl',
|
||||
'5xl',
|
||||
'6xl',
|
||||
'7xl',
|
||||
].includes(v),
|
||||
},
|
||||
modalStyle: { type: String, default: 'window' },
|
||||
role: { type: String, default: 'dialog' },
|
||||
useFocusTrap: { type: Boolean, default: true },
|
||||
})
|
||||
|
||||
const usesFocusTrap = ref(true)
|
||||
|
||||
const hasTrapFocus = computed(() => {
|
||||
return props.useFocusTrap && usesFocusTrap.value === true
|
||||
})
|
||||
|
||||
const { activate, deactivate } = useFocusTrap(modalContent, {
|
||||
immediate: false,
|
||||
allowOutsideClick: true,
|
||||
escapeDeactivates: false,
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
v => handleVisibilityChange(v)
|
||||
)
|
||||
|
||||
watch(hasTrapFocus, enable => {
|
||||
try {
|
||||
if (enable) {
|
||||
nextTick(() => activate())
|
||||
} else {
|
||||
deactivate()
|
||||
}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
})
|
||||
|
||||
useEventListener(document, 'keydown', e => {
|
||||
if (e.key === 'Escape' && props.show === true) {
|
||||
emit('close-via-escape', e)
|
||||
}
|
||||
})
|
||||
|
||||
const disableModalFocusTrap = () => {
|
||||
usesFocusTrap.value = false
|
||||
}
|
||||
|
||||
const enableModalFocusTrap = () => {
|
||||
usesFocusTrap.value = true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
Nova.$on('disable-focus-trap', disableModalFocusTrap)
|
||||
Nova.$on('enable-focus-trap', enableModalFocusTrap)
|
||||
|
||||
if (props.show === true) handleVisibilityChange(true)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.body.classList.remove('overflow-hidden')
|
||||
Nova.resumeShortcuts()
|
||||
|
||||
Nova.$off('disable-focus-trap', disableModalFocusTrap)
|
||||
Nova.$off('enable-focus-trap', enableModalFocusTrap)
|
||||
|
||||
usesFocusTrap.value = false
|
||||
})
|
||||
|
||||
const store = useStore()
|
||||
|
||||
async function handleVisibilityChange(showing) {
|
||||
if (showing === true) {
|
||||
emit('showing')
|
||||
document.body.classList.add('overflow-hidden')
|
||||
Nova.pauseShortcuts()
|
||||
|
||||
usesFocusTrap.value = true
|
||||
} else {
|
||||
usesFocusTrap.value = false
|
||||
|
||||
emit('closing')
|
||||
document.body.classList.remove('overflow-hidden')
|
||||
Nova.resumeShortcuts()
|
||||
}
|
||||
|
||||
store.commit('allowLeavingModal')
|
||||
}
|
||||
|
||||
const defaultAttributes = computed(() => {
|
||||
return omit(attrs, ['class'])
|
||||
})
|
||||
|
||||
const sizeClasses = computed(() => {
|
||||
return {
|
||||
sm: 'max-w-sm',
|
||||
md: 'max-w-md',
|
||||
lg: 'max-w-lg',
|
||||
xl: 'max-w-xl',
|
||||
'2xl': 'max-w-2xl',
|
||||
'3xl': 'max-w-3xl',
|
||||
'4xl': 'max-w-4xl',
|
||||
'5xl': 'max-w-5xl',
|
||||
'6xl': 'max-w-6xl',
|
||||
'7xl': 'max-w-7xl',
|
||||
}
|
||||
})
|
||||
|
||||
const contentClasses = computed(() => {
|
||||
let windowClasses = props.modalStyle === 'window' ? sizeClasses.value : {}
|
||||
|
||||
return filter([
|
||||
windowClasses[props.size] ?? null,
|
||||
props.modalStyle === 'fullscreen' ? 'h-full' : '',
|
||||
attrs.class,
|
||||
])
|
||||
})
|
||||
</script>
|
||||
11
nova/resources/js/components/Modals/ModalContent.vue
Normal file
11
nova/resources/js/components/Modals/ModalContent.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div class="py-3 px-8">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
//
|
||||
}
|
||||
</script>
|
||||
11
nova/resources/js/components/Modals/ModalFooter.vue
Normal file
11
nova/resources/js/components/Modals/ModalFooter.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div class="bg-gray-100 dark:bg-gray-700 px-6 py-3 flex">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
//
|
||||
}
|
||||
</script>
|
||||
14
nova/resources/js/components/Modals/ModalHeader.vue
Normal file
14
nova/resources/js/components/Modals/ModalHeader.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<Heading
|
||||
:level="3"
|
||||
class="border-b border-gray-100 dark:border-gray-700 py-4 px-8"
|
||||
>
|
||||
<slot />
|
||||
</Heading>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
//
|
||||
}
|
||||
</script>
|
||||
147
nova/resources/js/components/Modals/PreviewResourceModal.vue
Normal file
147
nova/resources/js/components/Modals/PreviewResourceModal.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<Modal
|
||||
:show="show"
|
||||
@close-via-escape="$emit('close')"
|
||||
role="alertdialog"
|
||||
size="2xl"
|
||||
:use-focus-trap="false"
|
||||
>
|
||||
<LoadingView
|
||||
:loading="loading"
|
||||
class="mx-auto bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden"
|
||||
>
|
||||
<slot>
|
||||
<ModalHeader class="flex items-center">
|
||||
<span>
|
||||
{{ modalTitle }}
|
||||
<span
|
||||
v-if="resource && resource.softDeleted"
|
||||
class="ml-auto bg-red-50 text-red-500 py-0.5 px-2 rounded-full text-xs"
|
||||
>
|
||||
{{ __('Soft Deleted') }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<Link
|
||||
dusk="detail-preview-button"
|
||||
:href="$url(`/resources/${resourceName}/${resourceId}`)"
|
||||
class="ml-auto"
|
||||
:alt="__('View :resource', { resource: title })"
|
||||
>
|
||||
<Icon type="arrow-right" />
|
||||
</Link>
|
||||
</ModalHeader>
|
||||
<ModalContent
|
||||
class="px-8 divide-y divide-gray-100 dark:divide-gray-800 -mx-3"
|
||||
>
|
||||
<template v-if="resource">
|
||||
<component
|
||||
:key="index"
|
||||
v-for="(field, index) in resource.fields"
|
||||
:index="index"
|
||||
:is="`detail-${field.component}`"
|
||||
:resource-name="resourceName"
|
||||
:resource-id="resourceId"
|
||||
:resource="resource"
|
||||
:field="field"
|
||||
/>
|
||||
|
||||
<div v-if="resource.fields.length == 0">
|
||||
{{ __('There are no fields to display.') }}
|
||||
</div>
|
||||
</template>
|
||||
</ModalContent>
|
||||
</slot>
|
||||
|
||||
<ModalFooter>
|
||||
<div class="ml-auto">
|
||||
<Button
|
||||
v-if="resource"
|
||||
dusk="confirm-preview-button"
|
||||
@click.prevent="$emit('close')"
|
||||
:label="__('Close')"
|
||||
/>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</LoadingView>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapProps } from '@/mixins'
|
||||
import { minimum } from '@/util'
|
||||
import { Button } from 'laravel-nova-ui'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Button,
|
||||
},
|
||||
|
||||
emits: ['close'],
|
||||
|
||||
props: {
|
||||
show: { type: Boolean, default: false },
|
||||
|
||||
...mapProps(['resourceName', 'resourceId']),
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
loading: true,
|
||||
title: null,
|
||||
resource: null,
|
||||
}),
|
||||
|
||||
async created() {
|
||||
await this.getResource()
|
||||
},
|
||||
|
||||
mounted() {
|
||||
Nova.$emit('close-dropdowns')
|
||||
},
|
||||
|
||||
methods: {
|
||||
getResource() {
|
||||
this.resource = null
|
||||
|
||||
return minimum(
|
||||
Nova.request().get(
|
||||
`/nova-api/${this.resourceName}/${this.resourceId}/preview`
|
||||
)
|
||||
)
|
||||
.then(({ data: { title, resource } }) => {
|
||||
this.title = title
|
||||
this.resource = resource
|
||||
this.loading = false
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.response.status >= 500) {
|
||||
Nova.$emit('error', error.response.data.message)
|
||||
return
|
||||
}
|
||||
|
||||
if (error.response.status === 404) {
|
||||
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}`)
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
modalTitle() {
|
||||
return `${this.__('Previewing')} ${this.title}`
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
80
nova/resources/js/components/Modals/RestoreResourceModal.vue
Normal file
80
nova/resources/js/components/Modals/RestoreResourceModal.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<Modal :show="show" size="sm">
|
||||
<form
|
||||
@submit.prevent="handleConfirm"
|
||||
class="bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden"
|
||||
style="width: 460px"
|
||||
>
|
||||
<slot>
|
||||
<ModalHeader v-text="__('Restore Resource')" />
|
||||
<ModalContent>
|
||||
<p class="leading-normal">
|
||||
{{ __('Are you sure you want to restore the selected resources?') }}
|
||||
</p>
|
||||
</ModalContent>
|
||||
</slot>
|
||||
|
||||
<ModalFooter>
|
||||
<div class="ml-auto">
|
||||
<LinkButton
|
||||
type="button"
|
||||
dusk="cancel-restore-button"
|
||||
@click.prevent="handleClose"
|
||||
class="mr-3"
|
||||
>
|
||||
{{ __('Cancel') }}
|
||||
</LinkButton>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
ref="confirmButton"
|
||||
dusk="confirm-restore-button"
|
||||
:loading="working"
|
||||
>
|
||||
{{ __('Restore') }}
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Button } from 'laravel-nova-ui'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Button,
|
||||
},
|
||||
|
||||
emits: ['confirm', 'close'],
|
||||
|
||||
props: {
|
||||
show: { type: Boolean, default: false },
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
working: false,
|
||||
}),
|
||||
|
||||
watch: {
|
||||
show(showing) {
|
||||
if (showing === false) {
|
||||
this.working = false
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit('close')
|
||||
this.working = false
|
||||
},
|
||||
|
||||
handleConfirm() {
|
||||
this.$emit('confirm')
|
||||
this.working = true
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user