Skip to content
Snippets Groups Projects
Commit 4ecec278 authored by Daniel Göbel's avatar Daniel Göbel
Browse files

Merge branch 'feature/8-10-filter-by-name' into 'development'

Filter Buckets and objects by their name

Closes #10 and #8

See merge request denbi/object-storage-access-ui!14
parents 35aae1b8 83a06cc3
No related branches found
No related tags found
2 merge requests!22Version 1.0.0,!14Filter Buckets and objects by their name
......@@ -17,7 +17,7 @@ import CopyObjectModal from "@/components/Modals/CopyObjectModal.vue";
import PermissionModal from "@/components/Modals/PermissionModal.vue";
import ObjectDetailModal from "@/components/Modals/ObjectDetailModal.vue";
import CreateFolderModal from "@/components/Modals/CreateFolderModal.vue";
import DeleteModal from "@/components/Modals/DeleteModal.vue"
import DeleteModal from "@/components/Modals/DeleteModal.vue";
import {
S3Client,
DeleteObjectCommand,
......@@ -114,16 +114,25 @@ type FolderTree = {
// Reactive State
// -----------------------------------------------------------------------------
const deleteObjectsState = reactive({
deletedItem: "",
potentialObjectToDelete: "",
deleteFolder: true,
} as {
deletedItem: string;
potentialObjectToDelete: string;
deleteFolder: boolean;
});
const objectState = reactive({
objects: [],
loading: true,
filterString: "",
bucketNotFoundError: false,
bucketPermissionError: false,
createdPermission: undefined,
deletedItem: "",
editObjectKey: "",
potentialObjectToDelete: "",
deleteFolder: true,
copyObject: {
key: "",
size: 0,
......@@ -139,13 +148,11 @@ const objectState = reactive({
} as {
objects: S3ObjectMetaInformation[];
loading: boolean;
filterString: string;
bucketNotFoundError: boolean;
bucketPermissionError: boolean;
createdPermission: undefined | BucketPermission;
deletedItem: string;
editObjectKey: string;
potentialObjectToDelete: string;
deleteFolder: boolean;
copyObject: S3ObjectMetaInformation;
viewDetailObject: S3ObjectMetaInformation;
});
......@@ -164,6 +171,15 @@ watch(
// Computed Properties
// -----------------------------------------------------------------------------
const filteredObjects: ComputedRef<(S3ObjectWithFolder | S3PseudoFolder)[]> =
computed(() => {
return objectState.filterString.length > 0
? visibleObjects.value.filter((obj) =>
obj.key.includes(objectState.filterString)
)
: visibleObjects.value;
});
const folderStructure: ComputedRef<FolderTree> = computed(() => {
/**
* Store the entire folder structure in a bucket in a tree-like data structure
......@@ -394,8 +410,8 @@ function objectCopied(copiedObject: S3ObjectMetaInformation) {
}
function deleteObject(key: string) {
objectState.potentialObjectToDelete = key;
objectState.deleteFolder = false;
deleteObjectsState.potentialObjectToDelete = key;
deleteObjectsState.deleteFolder = false;
}
/**
......@@ -411,7 +427,7 @@ function confirmedDeleteObject(key: string) {
.send(command)
.then(() => {
const splittedKey = key.split("/");
objectState.deletedItem = splittedKey[splittedKey.length - 1];
deleteObjectsState.deletedItem = splittedKey[splittedKey.length - 1];
successToast?.show();
objectState.objects = objectState.objects.filter(
(obj) => obj.key !== key
......@@ -443,8 +459,8 @@ async function downloadObject(key: string, bucket: string) {
}
function deleteFolder(folderPath: string) {
objectState.potentialObjectToDelete = folderPath;
objectState.deleteFolder = true;
deleteObjectsState.potentialObjectToDelete = folderPath;
deleteObjectsState.deleteFolder = true;
}
/**
......@@ -466,7 +482,7 @@ function confirmedDeleteFolder(folderPath: string) {
.send(command)
.then(() => {
const splittedPath = folderPath.split("/");
objectState.deletedItem = splittedPath[splittedPath.length - 2];
deleteObjectsState.deletedItem = splittedPath[splittedPath.length - 2];
successToast?.show();
objectState.objects = objectState.objects.filter(
(obj) => !obj.key.startsWith(folderPath)
......@@ -510,7 +526,7 @@ watch(
>
<div class="d-flex">
<div class="toast-body">
Successfully deleted {{ objectState.deletedItem }}
Successfully deleted {{ deleteObjectsState.deletedItem }}
</div>
<button
type="button"
......@@ -524,9 +540,13 @@ watch(
<DeleteModal
modalID="delete-object-modal"
modal-label="some-label"
:object-name-delete="objectState.potentialObjectToDelete"
:object-name-delete="deleteObjectsState.potentialObjectToDelete"
:back-modal-id="undefined"
@confirm-delete="objectState.deleteFolder ? confirmedDeleteFolder(objectState.potentialObjectToDelete) : confirmedDeleteObject(objectState.potentialObjectToDelete)"
@confirm-delete="
deleteObjectsState.deleteFolder
? confirmedDeleteFolder(deleteObjectsState.potentialObjectToDelete)
: confirmedDeleteObject(deleteObjectsState.potentialObjectToDelete)
"
/>
<!-- Navbar Breadcrumb -->
<nav aria-label="breadcrumb" class="fs-2">
......@@ -577,7 +597,7 @@ watch(
placeholder="Search Objects"
aria-label="Search Objects"
aria-describedby="objects-search-wrapping"
disabled
v-model.trim="objectState.filterString"
/>
</div>
</div>
......@@ -723,8 +743,8 @@ watch(
<td></td>
</tr>
</tbody>
<!-- Table body when no objects are in the bcuket -->
<tbody v-else-if="visibleObjects.length === 0">
<!-- Table body when no objects are in the bucket -->
<tbody v-else-if="filteredObjects.length === 0">
<tr>
<td colspan="4" class="text-center fst-italic fw-light">
No objects to display
......@@ -733,7 +753,7 @@ watch(
</tbody>
<!-- Table body when showing objects -->
<tbody v-else>
<tr v-for="obj in visibleObjects" :key="obj.key">
<tr v-for="obj in filteredObjects" :key="obj.key">
<th scope="row" class="text-truncate">
<!-- Show file name if row is an object -->
<div v-if="isS3Object(obj)">{{ obj.pseudoFileName }}</div>
......
......@@ -18,11 +18,13 @@ const authStore = useAuthStore();
const bucketsState = reactive({
buckets: [],
permissions: {},
filterString: "",
potentialDeleteBucketName: "",
loading: true,
} as {
buckets: BucketOut[];
loading: boolean;
filterString: string;
permissions: Record<string, BucketPermission>;
potentialDeleteBucketName: string;
});
......@@ -63,6 +65,14 @@ const currentPermission: ComputedRef<BucketPermission | undefined> = computed(
}
);
const filteredBuckets: ComputedRef<BucketOut[]> = computed(() => {
return bucketsState.filterString.length > 0
? bucketsState.buckets.filter((bucket) =>
bucket.name.includes(bucketsState.filterString)
)
: bucketsState.buckets;
});
function addBucket(bucket: BucketOut) {
bucketsState.buckets.push(bucket);
}
......@@ -143,25 +153,38 @@ onMounted(() => {
placeholder="Search Buckets"
aria-label="Search Buckets"
aria-describedby="buckets-search-wrapping"
disabled
v-model.trim="bucketsState.filterString"
/>
</div>
<div class="list-group mt-3">
<div v-if="!bucketsState.loading">
<bucket-list-item
v-for="bucket in bucketsState.buckets"
:key="bucket.name"
:active="
route.params.bucketName != null &&
route.params.bucketName === bucket.name
"
:bucket="bucket"
:loading="false"
:permission="bucketsState.permissions[bucket.name]"
@delete-bucket="deleteBucket"
@permission-deleted="(bucketName) => bucketDeleted(bucketName)"
/>
<div v-if="filteredBuckets.length > 0">
<bucket-list-item
v-for="bucket in filteredBuckets"
:key="bucket.name"
:active="
route.params.bucketName != null &&
route.params.bucketName === bucket.name
"
:bucket="bucket"
:loading="false"
:permission="bucketsState.permissions[bucket.name]"
@delete-bucket="deleteBucket"
@permission-deleted="(bucketName) => bucketDeleted(bucketName)"
/>
</div>
<div v-else class="text-center fs-2 mt-5">
<bootstrap-icon
icon="search"
class="mb-2"
:width="56"
:height="56"
style="color: var(--bs-secondary)"
fill="currentColor"
/><br />
Could not find any Buckets
</div>
</div>
<div v-else>
<bucket-list-item
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment