<script setup lang="ts"> import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import { Modal, Toast } from "bootstrap"; import { onMounted, reactive, watch } from "vue"; import type { _Object as S3Object } from "@aws-sdk/client-s3"; import { useBucketStore } from "@/stores/buckets"; import { useS3ObjectStore } from "@/stores/s3objects"; const objectRepository = useS3ObjectStore(); const props = defineProps<{ modalID: string; srcObject: S3Object; srcBucket: string; }>(); const formState = reactive<{ destKey: string; destBucket: string; uploading: boolean; }>({ destKey: "", destBucket: "", uploading: false, }); const bucketRepository = useBucketStore(); const randomIDSuffix = Math.random().toString(16).substring(2, 8); let copyModal: Modal | null = null; let successToast: Toast | null = null; let errorToast: Toast | null = null; function getFileName(key?: string): string { if (key == undefined) { return ""; } const splittedKey = key.split("/"); return splittedKey[splittedKey.length - 1]; } function copyObject() { if (props.srcObject.Key == undefined) { return; } formState.uploading = true; objectRepository .copyObject( props.srcBucket, props.srcObject, formState.destBucket, formState.destKey, ) .then(() => { copyModal?.hide(); successToast?.show(); formState.destBucket = ""; }) .catch((e) => { console.error(e); errorToast?.show(); }) .finally(() => { formState.uploading = false; }); } function modalClosed() { formState.destBucket = ""; } watch( () => props.srcObject.Key, (newKey) => { formState.destKey = newKey ?? ""; }, ); onMounted(() => { copyModal = new Modal("#" + props.modalID); successToast = new Toast("#successToast-" + randomIDSuffix); errorToast = new Toast("#errorToast-" + randomIDSuffix); }); </script> <template> <div class="toast-container position-fixed top-toast end-0 p-3"> <div role="alert" aria-live="assertive" aria-atomic="true" class="toast text-bg-success align-items-center border-0" data-bs-autohide="true" :id="'successToast-' + randomIDSuffix" > <div class="d-flex"> <div class="toast-body">Successfully copied file</div> <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close" ></button> </div> </div> </div> <div class="toast-container position-fixed top-toast end-0 p-3"> <div role="alert" aria-live="assertive" aria-atomic="true" class="toast text-bg-danger align-items-center border-0" data-bs-autohide="true" :id="'errorToast-' + randomIDSuffix" > <div class="d-flex"> <div class="toast-body"> There has been some Error.<br /> Try again later </div> <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close" ></button> </div> </div> </div> <bootstrap-modal :modalID="modalID" :static-backdrop="true" modal-label="Copy Object Modal" v-on="{ 'hidden.bs.modal': modalClosed }" > <template v-slot:header> <h4>Copy file {{ getFileName(props.srcObject.Key) }}</h4> </template> <template v-slot:body> <div class="container-fluid"> <div class="row"> <form class="col-7" :id="'copyObjectForm' + randomIDSuffix" @submit.prevent="copyObject" > <div class="mb-3"> <label :for="'destinationBucket' + randomIDSuffix" class="form-label" > Destination Bucket * </label> <select class="form-select text-lowercase" :id="'destinationBucket' + randomIDSuffix" required v-model="formState.destBucket" > <option disabled selected>Select one...</option> <option v-for="bucket in bucketRepository.writableBuckets" :key="bucket.name" :value="bucket.name" > {{ bucket.name }} </option> </select> </div> <div class="mb-3"> <label :for="'objectKey' + randomIDSuffix" class="form-label" >Destination Filename *</label > <input type="text" class="form-control" :id="'objectKey' + randomIDSuffix" required v-model="formState.destKey" /> </div> </form> <div class="col-5"> You can copy objects. You have to create destination buckets prior to copy.<br /> You can specify folder by using '/' at destination object field. For example, if you want to copy object under the folder named 'folder1', you need to specify destination object like 'folder1/[your object name]'. </div> </div> </div> </template> <template v-slot:footer> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> Close </button> <button :disabled="formState.uploading" type="submit" :form="'copyObjectForm' + randomIDSuffix" class="btn btn-primary" > <span v-if="formState.uploading" class="spinner-border spinner-border-sm" role="status" aria-hidden="true" ></span> Copy </button> </template> </bootstrap-modal> </template> <style scoped></style>