Skip to content
Snippets Groups Projects

Display buckets and their objects

Merged Daniel Göbel requested to merge feature/5-display-buckets into development
2 files
+ 7
1
Compare changes
  • Side-by-side
  • Inline
Files
2
+ 241
0
<script setup lang="ts">
import { useRoute } from "vue-router";
import { onMounted, reactive, watch, computed } from "vue";
import type { S3ObjectMetaInformation } from "@/client";
import { ObjectService } from "@/client";
import BootstrapIcon from "@/components/BootstrapIcon.vue";
import fileSize from "filesize";
import dayjs from "dayjs";
const route = useRoute();
const objectState = reactive({
objects: [],
loading: true,
bucket_not_found_error: false,
bucket_permission_error: false,
} as {
objects: S3ObjectMetaInformation[];
loading: boolean;
bucket_not_found_error: boolean;
bucket_permission_error: boolean;
});
watch(
() => route.params,
(newRouteParams, oldRouteParams) => {
if (
newRouteParams.bucket_name &&
oldRouteParams.bucket_name !== newRouteParams.bucket_name
) {
update_objects(newRouteParams.bucket_name as string);
}
}
);
const sub_folder_in_url = computed(() => route.params.sub_folders.length > 0);
const error_loading_objects = computed(
() =>
objectState.bucket_permission_error || objectState.bucket_not_found_error
);
onMounted(() => {
update_objects(route.params.bucket_name as string);
});
function update_objects(bucket_name: string) {
objectState.bucket_not_found_error = false;
objectState.bucket_permission_error = false;
objectState.loading = true;
ObjectService.objectGetBucketObjects(bucket_name)
.then((objs) => {
objectState.objects = objs;
})
.catch((error) => {
if (error.status === 404) {
objectState.bucket_not_found_error = true;
} else if (error.status == 403) {
objectState.bucket_permission_error = true;
}
})
.finally(() => {
objectState.loading = false;
});
}
</script>
<template>
<nav aria-label="breadcrumb" class="fs-2">
<ol class="breadcrumb">
<li class="breadcrumb-item" :class="{ active: sub_folder_in_url }">
<router-link
v-if="sub_folder_in_url"
:to="{
name: 'bucket',
params: { bucket_name: route.params.bucket_name, sub_folders: [] },
}"
>{{ route.params.bucket_name }}</router-link
>
<span v-else>{{ route.params.bucket_name }}</span>
</li>
<li
class="breadcrumb-item"
v-for="(folder, index) in route.params.sub_folders"
:key="folder"
:class="{ active: index === route.params.sub_folders.length }"
>
<router-link
v-if="index !== route.params.sub_folders.length - 1"
:to="{
name: 'bucket',
params: {
bucket_name: route.params.bucket_name,
sub_folders: route.params.sub_folders.slice(0, index + 1),
},
}"
>{{ folder }}</router-link
>
<span v-else>{{ folder }}</span>
</li>
</ol>
</nav>
<div class="input-group mt-2">
<span class="input-group-text" id="objects-search-wrapping"
><bootstrap-icon icon="search" :width="16" :height="16"
/></span>
<input
type="text"
class="form-control"
placeholder="Search Objects"
aria-label="Search Objects"
aria-describedby="objects-search-wrapping"
disabled
/>
</div>
<button
type="button"
class="btn btn-secondary m-2"
:disabled="error_loading_objects"
>
<bootstrap-icon icon="upload" :width="16" :height="16" fill="white" />
<span class="visually-hidden">Upload Object</span>
</button>
<button
type="button"
class="btn btn-secondary m-2"
:disabled="error_loading_objects"
>
<bootstrap-icon
icon="person-plus-fill"
:width="16"
:height="16"
fill="white"
/>
<span class="visually-hidden">Add Bucket Permission</span>
</button>
<button
type="button"
class="btn btn-secondary m-2"
:disabled="error_loading_objects"
>
<bootstrap-icon icon="plus-lg" :width="16" :height="16" fill="white" />
Folder
<span class="visually-hidden">Add Folder</span>
</button>
<div class="pt-3">
<div v-if="objectState.bucket_not_found_error">
<p>Bucket not found</p>
</div>
<div v-else-if="objectState.bucket_permission_error">
<p>No permission for this bucket</p>
</div>
<div v-else>
<table
class="table table-dark table-striped table-hover caption-top align-middle"
>
<caption>
Displaying
{{
objectState.loading ? 0 : objectState.objects.length
}}
Objects
</caption>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Last Accessed</th>
<th scope="col">Size</th>
<th scope="col"></th>
</tr>
</thead>
<tbody v-if="objectState.loading">
<tr v-for="n in 5" :key="n" class="placeholder-glow">
<th scope="row">
<span class="placeholder w-100 bg-secondary"></span>
</th>
<td><span class="placeholder w-50 bg-secondary"></span></td>
<td><span class="placeholder w-50 bg-secondary"></span></td>
<td></td>
</tr>
</tbody>
<tbody v-else-if="objectState.objects.length === 0">
<tr>
<td colspan="4" class="text-center text-secondary">
<i>No objects to display</i>
</td>
</tr>
</tbody>
<tbody v-else>
<tr v-for="obj in objectState.objects" :key="obj.key">
<th scope="row" class="text-truncate">{{ obj.key }}</th>
<td>{{ dayjs(obj.last_modified).fromNow() }}</td>
<td>{{ fileSize(obj.size) }}</td>
<td class="text-end">
<div
class="btn-group btn-group-sm dropdown-center dropdown-menu-start"
>
<button type="button" class="btn btn-secondary">
Download
</button>
<button
type="button"
class="btn btn-secondary dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu dropdown-menu-dark">
<li>
<button class="dropdown-item" type="button">Details</button>
</li>
<li>
<button class="dropdown-item" type="button">Edit</button>
</li>
<li>
<button class="dropdown-item" type="button">Copy</button>
</li>
<li>
<button class="dropdown-item text-danger" type="button">
<bootstrap-icon
icon="trash-fill"
class="text-danger"
:width="13"
:height="13"
fill="currentColor"
/>
<span class="ms-1">Delete</span>
</button>
</li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<style scoped></style>
Loading