<script setup lang="ts"> import type { BucketIn } from "@/client/s3proxy"; import { reactive, onMounted, ref } from "vue"; import BootstrapModal from "@/components/modals/BootstrapModal.vue"; import { useRouter } from "vue-router"; import { Modal } from "bootstrap"; import { useBucketStore } from "@/stores/buckets"; const router = useRouter(); const bucketRepository = useBucketStore(); const bucketCreateForm = ref<HTMLFormElement | undefined>(undefined); const bucketNameElement = ref<HTMLInputElement | undefined>(undefined); const bucket = reactive<BucketIn>({ name: "", description: "" }); const formState = reactive<{ validated: boolean; bucketNameTaken: boolean; loading: boolean; }>({ validated: false, bucketNameTaken: false, loading: false, }); const props = defineProps<{ modalID: string; }>(); let createBucketModal: Modal | null = null; onMounted(() => { createBucketModal = new Modal("#" + props.modalID); }); function createBucket() { formState.validated = true; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion formState.bucketNameTaken = false; bucket.description = bucket.description.trim(); bucket.name = bucket.name.trim(); bucketNameElement.value?.setCustomValidity(""); if (bucketCreateForm.value?.checkValidity()) { formState.loading = true; bucketRepository .createBucket(bucket) .then((createdBucket) => { createBucketModal?.hide(); bucket.name = ""; bucket.description = ""; formState.bucketNameTaken = false; formState.validated = false; router.push({ name: "bucket", params: { bucketName: createdBucket.name, subFolders: [] }, }); }) .catch((error) => { if ( error.status === 400 && error.body["detail"] === "Bucket name is already taken" ) { formState.bucketNameTaken = true; bucketNameElement.value?.setCustomValidity( "Bucket name is already taken", ); } }) .finally(() => { formState.loading = false; }); } } function modalClosed() { formState.validated = false; formState.bucketNameTaken = false; bucketNameElement.value?.setCustomValidity(""); } </script> <template> <bootstrap-modal :modalId="modalID" :static-backdrop="true" modal-label="Create Bucket Modal" v-on="{ 'hidden.bs.modal': modalClosed }" > <template v-slot:header> Create new S3 Bucket</template> <template v-slot:body> <form id="bucketCreateForm" :class="{ 'was-validated': formState.validated }" ref="bucketCreateForm" > <div class="mb-3"> <label for="bucketNameInput" class="form-label">Bucket Name</label> <div class="input-group"> <input type="text" class="form-control" id="bucketNameInput" required minlength="3" maxlength="63" pattern="(?!(^(\d+\.){3}\d+$))^([a-z0-9](?:[a-z0-9\-]*[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?$" v-model="bucket.name" ref="bucketNameElement" /> <div class="invalid-feedback"> <div v-if="formState.bucketNameTaken"> Bucket name already taken. </div> <div> Requirements <ul> <li>At least 3 Characters long</li> <li>Unique in CloWM</li> <li> Only lowercase letters, numbers, single periods (.) and hyphen (-) </li> <li>Start and end with letter or number</li> <li> A letter or number must be before and after a period (.) </li> </ul> </div> </div> </div> </div> <div class="mb-3"> <label for="bucketDescriptionInput" class="form-label"> Description </label> <div class="input-group"> <textarea class="form-control" id="bucketDescriptionInput" required rows="3" minlength="32" maxlength="65536" v-model="bucket.description" placeholder="Describe the purpose of the bucket" ></textarea> <div class="invalid-feedback"> Requirements <ul> <li>At least 32 Characters long</li> </ul> </div> </div> </div> </form> </template> <template v-slot:footer> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> Close </button> <button type="submit" form="bucketCreateForm" class="btn btn-primary" :disabled="formState.loading" @click.prevent="createBucket" > <span v-if="formState.loading" class="spinner-border spinner-border-sm" role="status" aria-hidden="true" ></span> Save </button> </template> </bootstrap-modal> </template> <style scoped></style>