<script setup>
import { ref, watch, onMounted } from "vue";
import route from "ziggy-js";
import axios from "axios";
import Folder from "@components/Bucket/Partials/Folder.vue";
import File from "@components/Bucket/Partials/File.vue";
import OptionMenu from "@components/Bucket/Partials/OptionMenu.vue";
import BucketForm from "@components/Bucket/Form/BucketForm.vue";
import SlideOver from "@components/UI/SlideOver.vue";
import DeleteModal from "@components/Bucket/Modals/DeleteModal.vue";
import { DialogTitle } from "@headlessui/vue";
import { ExclamationCircleIcon } from "@heroicons/vue/24/outline/index.js";
import FileUploadModal from "@components/Bucket/Modals/FileUploadModal.vue";
import {
CloudArrowUpIcon,
HomeIcon,
FolderPlusIcon
} from "@heroicons/vue/20/solid/index.js";
import NewFolderModal from "@components/Bucket/Modals/NewFolderModal.vue";
const props = defineProps({
bucket: {
type: Object,
default: () => {}
},
folderCols: {
type: [Number, String],
default: '6',
},
fileCols: {
type: [Number, String],
default: '4',
},
hideMenu: {
type: Boolean,
default: false,
},
height: {
type: String,
default: 'screen',
},
});
const emit = defineEmits(['currentFolder', 'deleted']);
const storage = ref({
current: {
path: '/',
name: 'root',
},
files: [],
folders: [],
breadcrumbs: [],
});
const loading = ref(false);
const showBucketEditForm = ref(false);
const openBucketDeleteModal = ref(false);
const openFileUploadModal = ref(false);
const openNewFolderModal = ref(false);
const loadData = async (path = '/') => {
loading.value = true;
document.body.style.cursor = 'wait';
try {
const { data } = await axios.get(route('buckets.files', {
bucket: props.bucket.id,
path,
}));
storage.value = data;
} catch (error) {
console.error(error);
} finally {
document.body.style.cursor = 'default';
loading.value = false;
}
emit('currentFolder', storage.value.current);
};
watch(() => props.bucket, () => {
loadData();
});
const setupWebSocketListeners = () => {
const fileEventsChannel = Echo.channel('file-events');
fileEventsChannel.listen('FileMovedEvent', (event) => {
loadData(storage.value.current.path);
});
};
onMounted(() => {
emit('currentFolder', storage.value.current);
loadData();
setupWebSocketListeners();
});
</script>
<template>
<!-- Bucket Edit Form -->
<SlideOver
v-if="!hideMenu"
:show="showBucketEditForm"
@close="showBucketEditForm = false"
>
<BucketForm
:url="route('buckets.update', {
bucket: props.bucket.id
})"
:title="$t('buckets.form.edit.title')"
:description="$t('buckets.form.edit.description')"
:bucket="props.bucket"
edit
@cancel="showBucketEditForm = false"
@saved="showBucketEditForm = false"
/>
</SlideOver>
<!-- Bucket Delete Modal -->
<DeleteModal
v-if="!hideMenu"
:open="openBucketDeleteModal"
:delete-url="route('buckets.destroy', {
bucket: bucket.id
})"
@close="openBucketDeleteModal = false"
@deleted="emit('deleted')"
>
<div class="bg-white p-4 ">
<div class="text-center sm:text-left">
<DialogTitle
as="h3"
class="text-base font-semibold leading-6 text-gray-900"
>
{{ $t('modals.delete_title') }}
</DialogTitle>
<div class="mt-3">
<p class="text-sm text-gray-500">
{{ $t('modals.delete_bucket') }}
</p>
</div>
<div class="inline-flex gap-2 mt-3">
<ExclamationCircleIcon class="w-5 h-5 text-gray-500" />
<p class="text-sm text-gray-500">
{{ bucket.name }}
</p>
</div>
</div>
</div>
</DeleteModal>
<!-- File Upload Modal -->
<FileUploadModal
v-if="!loading"
:open="openFileUploadModal"
:upload-url="route('buckets.files.upload', {
bucket: props.bucket.id,
path: storage.current.path,
})"
@uploaded="loadData(storage.current.path)"
@close="openFileUploadModal = false"
/>
<!-- New Folder Modal -->
<NewFolderModal
v-if="!loading"
:open="openNewFolderModal"
:current-path="storage.current.path"
:create-url="route('buckets.folders.create', {
bucket: props.bucket.id,
})"
@created="loadData(storage.current.path)"
@close="openNewFolderModal = false"
/>
<div class="bg-white relative">
<div class="flex gap-3 items-center justify-between sticky top-0 bg-gray-100 divided-x">
<!-- Breadcrumbs -->
<div class="px-5 py-3 overflow-x-auto">
<nav
class="flex"
aria-label="Breadcrumb"
>
<ol
role="list"
class="flex items-center space-x-1"
>
<li
v-if="storage.breadcrumbs.length < 1"
@click="loadData('/')"
>
<div>
<span class="text-gray-400 hover:text-gray-500 cursor-pointer">
<HomeIcon
class="h-5 w-5 flex-shrink-0"
aria-hidden="true"
/>
<span class="sr-only">Home</span>
</span>
</div>
</li>
<li
v-for="(breadcrumb, index) in storage.breadcrumbs"
v-else
:key="index"
>
<div v-if="breadcrumb.name === 'root'">
<span
class="text-gray-400 hover:text-gray-500 cursor-pointer"
@click="loadData(breadcrumb.path)"
>
<HomeIcon
class="h-5 w-5 flex-shrink-0"
aria-hidden="true"
/>
<span class="sr-only">Home</span>
</span>
</div>
<div
v-else
class="flex items-center"
>
<svg
class="h-5 w-5 flex-shrink-0 text-gray-300"
fill="currentColor"
viewBox="0 0 20 20"
aria-hidden="true"
>
<path d="M5.555 17.776l8-16 .894.448-8 16-.894-.448z" />
</svg>
<span
class="ml-1 text-sm font-medium text-gray-500 hover:text-gray-700 cursor-pointer"
@click="loadData(breadcrumb.path)"
>
{{ breadcrumb.name }}
</span>
</div>
</li>
<li>
<div class="flex items-center">
<svg
class="h-5 w-5 flex-shrink-0 text-gray-300"
fill="currentColor"
viewBox="0 0 20 20"
aria-hidden="true"
>
<path d="M5.555 17.776l8-16 .894.448-8 16-.894-.448z" />
</svg>
<span
v-if="storage.current.path !== '/'"
class="ml-1 text-sm font-medium text-gray-500 hover:text-gray-700 cursor-pointer"
@click="loadData(storage.current.path)"
>
{{ storage.current.name }}
</span>
</div>
</li>
</ol>
</nav>
</div>
<!-- Menu -->
<div
v-if="!hideMenu"
class="px-5 flex gap-3 items-center divide-x"
>
<div
:class="['text-gray-700', 'flex px-2 py-2 text-sm', 'cursor-pointer']"
@click="openNewFolderModal = true"
>
<FolderPlusIcon
class="mr-1.5 h-5 w-5 text-gray-400"
aria-hidden="true"
/>
<span>
{{ $t('buttons.new_folder') }}
</span>
</div>
<div
:class="['text-gray-700', 'flex px-2 py-2 text-sm', 'cursor-pointer']"
@click="openFileUploadModal = true"
>
<CloudArrowUpIcon
class="mr-1.5 h-5 w-5 text-gray-400"
aria-hidden="true"
/>
<span>
{{ $t('buttons.upload') }}
</span>
</div>
<OptionMenu
class="pt-2"
hide-move
hide-upload
hide-view
@edit="showBucketEditForm = true"
@delete="openBucketDeleteModal = true"
/>
</div>
</div>
<div
class="overflow-y-auto px-5 py-3 space-y-5"
:class="[`h-${height}`]"
>
<!-- Folder List -->
<div
v-if="storage.folders.length"
class="space-y-2"
>
<span class="font-semibold text-md">
{{ $t('file_manager.folders') }}
</span>
<div :class="['grid gap-2', folderCols]">
<div
v-for="(folder, index) in storage.folders"
:key="index"
@dblclick="loadData(folder.path)"
>
<Folder
:bucket="bucket"
:folder="folder"
:hide-menu="hideMenu"
@deleted="loadData(storage.current.path)"
@moved="loadData(storage.current.path)"
/>
</div>
</div>
</div>
<!-- File List -->
<div
v-if="storage.files.length"
class="space-y-2"
>
<span class="font-semibold text-md">
{{ $t('file_manager.files') }}
</span>
<div :class="['grid gap-2', fileCols]">
<div
v-for="(file, index) in storage.files"
:key="index"
class="text-sm text-gray-700"
>
<File
:file="file"
:bucket="bucket"
:hide-menu="hideMenu"
@deleted="loadData(storage.current.path)"
@moved="loadData(storage.current.path)"
@renamed="loadData(storage.current.path)"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>
|