Skip to content
Snippets Groups Projects
Verified Commit 6055aa6c authored by Daniel Göbel's avatar Daniel Göbel
Browse files

Improve UI for making a bucket public

parent f644a949
No related branches found
No related tags found
1 merge request!108Resolve "Add checkbox for public buckets"
...@@ -5,8 +5,8 @@ import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; ...@@ -5,8 +5,8 @@ import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import PermissionModal from "@/components/object-storage/modals/PermissionModal.vue"; import PermissionModal from "@/components/object-storage/modals/PermissionModal.vue";
import BucketDetailModal from "@/components/object-storage/modals/BucketDetailModal.vue"; import BucketDetailModal from "@/components/object-storage/modals/BucketDetailModal.vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, reactive, ref } from "vue";
import { Tooltip } from "bootstrap"; import { Toast, Tooltip } from "bootstrap";
import { useBucketStore } from "@/stores/buckets"; import { useBucketStore } from "@/stores/buckets";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useAuthStore } from "@/stores/users"; import { useAuthStore } from "@/stores/users";
...@@ -15,6 +15,7 @@ import { useNameStore } from "@/stores/names"; ...@@ -15,6 +15,7 @@ import { useNameStore } from "@/stores/names";
import { environment } from "@/environment"; import { environment } from "@/environment";
import { useS3ObjectStore } from "@/stores/s3objects"; import { useS3ObjectStore } from "@/stores/s3objects";
import { filesize } from "filesize"; import { filesize } from "filesize";
import BootstrapToast from "@/components/BootstrapToast.vue";
const props = defineProps<{ const props = defineProps<{
active: boolean; active: boolean;
...@@ -32,6 +33,17 @@ const bucketRepository = useBucketStore(); ...@@ -32,6 +33,17 @@ const bucketRepository = useBucketStore();
const objectRepository = useS3ObjectStore(); const objectRepository = useS3ObjectStore();
const router = useRouter(); const router = useRouter();
let successToast: Toast | null;
let errorToast: Toast | null;
const requestState = reactive<{
error: string;
loading: boolean;
}>({
error: "",
loading: false,
});
const permission = computed<BucketPermissionOut | undefined>( const permission = computed<BucketPermissionOut | undefined>(
() => permissionRepository.ownPermissions[props.bucket.name], () => permissionRepository.ownPermissions[props.bucket.name],
); );
...@@ -56,12 +68,21 @@ function permissionDeleted() { ...@@ -56,12 +68,21 @@ function permissionDeleted() {
} }
function toggleBucketPublicState() { function toggleBucketPublicState() {
requestState.loading = true;
bucketRepository bucketRepository
.togglePublicState(props.bucket.name, !props.bucket.public) .togglePublicState(props.bucket.name, !props.bucket.public)
.catch(() => { .then(() => {
successToast?.show();
})
.catch((err) => {
requestState.error = err.toString();
if (publicCheckbox.value) { if (publicCheckbox.value) {
publicCheckbox.value.checked = props.bucket.public; publicCheckbox.value.checked = props.bucket.public;
} }
errorToast?.show();
})
.finally(() => {
requestState.loading = false;
}); });
} }
...@@ -70,11 +91,28 @@ onMounted(() => { ...@@ -70,11 +91,28 @@ onMounted(() => {
new Tooltip("#tooltip-" + randomIDSuffix); new Tooltip("#tooltip-" + randomIDSuffix);
new Tooltip("#ownBucketIcon-" + randomIDSuffix); new Tooltip("#ownBucketIcon-" + randomIDSuffix);
new Tooltip("#sharedBucketIcon-" + randomIDSuffix); new Tooltip("#sharedBucketIcon-" + randomIDSuffix);
successToast = new Toast("#success-public-bucket-" + randomIDSuffix);
errorToast = new Toast("#error-public-bucket-" + randomIDSuffix);
} }
}); });
</script> </script>
<template> <template>
<bootstrap-toast
:toast-id="'success-public-bucket-' + randomIDSuffix"
v-if="!loading"
>
Bucket {{ bucket.name }} is now {{ bucket.public ? "public" : "private" }}
</bootstrap-toast>
<bootstrap-toast
:toast-id="'error-public-bucket-' + randomIDSuffix"
color-class="danger"
v-if="!loading"
>
Error making the bucket {{ bucket.name }}
{{ !bucket.public ? "public" : "private" }}:<br />
{{ requestState.error }}
</bootstrap-toast>
<permission-modal <permission-modal
v-if="permission != undefined && props.active" v-if="permission != undefined && props.active"
:modalId="'view-permission-modal' + randomIDSuffix" :modalId="'view-permission-modal' + randomIDSuffix"
...@@ -236,30 +274,35 @@ onMounted(() => { ...@@ -236,30 +274,35 @@ onMounted(() => {
</tr> </tr>
<tr v-if="bucket.owner_constraint == undefined"> <tr v-if="bucket.owner_constraint == undefined">
<th scope="row"> <th scope="row">
<label <div
:for="'public-checkbox-' + randomIDSuffix" :class="{ 'form-check': !loading && permission == undefined }"
class="fw-bold"
>
Public</label
> >
<input
v-if="!loading && permission == undefined"
ref="publicCheckbox"
class="form-check-input"
type="checkbox"
:disabled="requestState.loading"
:checked="bucket.public"
:id="'public-checkbox-' + randomIDSuffix"
@change="toggleBucketPublicState"
/>
<label
:for="'public-checkbox-' + randomIDSuffix"
class="fw-bold"
>
Public</label
>
</div>
</th> </th>
<td> <td>
<input
ref="publicCheckbox"
class="form-check-input"
:disabled="loading || permission != undefined"
type="checkbox"
:checked="bucket.public"
:id="'public-checkbox-' + randomIDSuffix"
@change="toggleBucketPublicState"
/>
<a <a
class="ms-4"
v-if="bucket.public" v-if="bucket.public"
target="_blank" target="_blank"
:href="environment.S3_URL + '/' + bucket.name" :href="environment.S3_URL + '/' + bucket.name"
>Show</a >Link</a
> >
<span v-else>Disabled</span>
</td> </td>
</tr> </tr>
</tbody> </tbody>
......
...@@ -34,11 +34,11 @@ const resourceVersion = computed<ResourceVersionOut | undefined>( ...@@ -34,11 +34,11 @@ const resourceVersion = computed<ResourceVersionOut | undefined>(
<template #body> <template #body>
<h5>Resource</h5> <h5>Resource</h5>
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col-8"> <div class="col-7">
<label <label
for="resource-version-info-modal-resource-id" for="resource-version-info-modal-resource-id"
class="form-label" class="form-label"
>ID</label >Resource ID</label
> >
<div class="input-group"> <div class="input-group">
<input <input
...@@ -54,7 +54,7 @@ const resourceVersion = computed<ResourceVersionOut | undefined>( ...@@ -54,7 +54,7 @@ const resourceVersion = computed<ResourceVersionOut | undefined>(
/></span> /></span>
</div> </div>
</div> </div>
<div class="col-4"> <div class="col-5">
<label <label
for="resource-version-info-modal-resource-name" for="resource-version-info-modal-resource-name"
class="form-label" class="form-label"
...@@ -72,7 +72,7 @@ const resourceVersion = computed<ResourceVersionOut | undefined>( ...@@ -72,7 +72,7 @@ const resourceVersion = computed<ResourceVersionOut | undefined>(
</div> </div>
</div> </div>
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col-8"> <div class="col-7">
<label <label
for="resource-version-info-modal-maintainer-id" for="resource-version-info-modal-maintainer-id"
class="form-label" class="form-label"
...@@ -92,7 +92,7 @@ const resourceVersion = computed<ResourceVersionOut | undefined>( ...@@ -92,7 +92,7 @@ const resourceVersion = computed<ResourceVersionOut | undefined>(
/></span> /></span>
</div> </div>
</div> </div>
<div class="col-4"> <div class="col-5">
<label <label
for="resource-version-info-modal-maintainer-name" for="resource-version-info-modal-maintainer-name"
class="form-label" class="form-label"
...@@ -143,7 +143,7 @@ const resourceVersion = computed<ResourceVersionOut | undefined>( ...@@ -143,7 +143,7 @@ const resourceVersion = computed<ResourceVersionOut | undefined>(
</div> </div>
<h5>Resource Version</h5> <h5>Resource Version</h5>
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col-8"> <div class="col-7">
<label <label
for="resource-version-info-modal-resource-version-id" for="resource-version-info-modal-resource-version-id"
class="form-label" class="form-label"
...@@ -163,7 +163,7 @@ const resourceVersion = computed<ResourceVersionOut | undefined>( ...@@ -163,7 +163,7 @@ const resourceVersion = computed<ResourceVersionOut | undefined>(
/></span> /></span>
</div> </div>
</div> </div>
<div class="col-4"> <div class="col-5">
<label <label
for="resource-version-info-modal-resource-version-release" for="resource-version-info-modal-resource-version-release"
class="form-label" class="form-label"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import type { WorkflowOut, WorkflowVersion } from "@/client/workflow"; import type { WorkflowOut, WorkflowVersion } from "@/client/workflow";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue"; import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { onMounted, ref, computed } from "vue"; import { onMounted, computed } from "vue";
import { Tooltip } from "bootstrap"; import { Tooltip } from "bootstrap";
import { latestVersion as calculateLatestVersion } from "@/utils/Workflow"; import { latestVersion as calculateLatestVersion } from "@/utils/Workflow";
...@@ -12,7 +12,6 @@ const props = defineProps<{ ...@@ -12,7 +12,6 @@ const props = defineProps<{
}>(); }>();
const randomIDSuffix: string = Math.random().toString(16).substring(2, 8); const randomIDSuffix: string = Math.random().toString(16).substring(2, 8);
const truncateDescription = ref<boolean>(true);
const latestVersion = computed<WorkflowVersion | undefined>(() => const latestVersion = computed<WorkflowVersion | undefined>(() =>
calculateLatestVersion(props.workflow.versions), calculateLatestVersion(props.workflow.versions),
); );
...@@ -55,18 +54,11 @@ onMounted(() => { ...@@ -55,18 +54,11 @@ onMounted(() => {
class="img-fluid float-end icon" class="img-fluid float-end icon"
/> />
</div> </div>
<p class="card-text" :class="{ 'text-truncate': truncateDescription }"> <p class="card-text">
<span v-if="props.loading" class="placeholder-glow" <span v-if="props.loading" class="placeholder-glow"
><span class="placeholder col-12"></span ><span class="placeholder col-12"></span
></span> ></span>
<span <span v-else>{{ props.workflow.short_description }}</span>
v-else
@click="truncateDescription = false"
:class="{
'cursor-pointer': truncateDescription,
}"
>{{ props.workflow.short_description }}</span
>
</p> </p>
<div class="d-flex justify-content-between mb-0"> <div class="d-flex justify-content-between mb-0">
<div v-if="props.loading" class="placeholder-glow w-50"> <div v-if="props.loading" class="placeholder-glow w-50">
......
...@@ -115,6 +115,10 @@ export const useResourceStore = defineStore({ ...@@ -115,6 +115,10 @@ export const useResourceStore = defineStore({
return ResourceService.resourceListSyncRequests() return ResourceService.resourceListSyncRequests()
.then((requests) => { .then((requests) => {
this.__syncRequestsFetched = true; this.__syncRequestsFetched = true;
const userStore = useAuthStore();
userStore.fetchUsernames(
requests.map((request) => request.requester_id),
);
const newMapping: Record<string, UserSynchronizationRequestOut> = {}; const newMapping: Record<string, UserSynchronizationRequestOut> = {};
for (const request of requests) { for (const request of requests) {
newMapping[request.resource_version_id] = request; newMapping[request.resource_version_id] = request;
...@@ -136,6 +140,10 @@ export const useResourceStore = defineStore({ ...@@ -136,6 +140,10 @@ export const useResourceStore = defineStore({
searchString, searchString,
_public, _public,
).then((resources) => { ).then((resources) => {
const userStore = useAuthStore();
userStore.fetchUsernames(
resources.map((resource) => resource.maintainer_id),
);
const nameStore = useNameStore(); const nameStore = useNameStore();
for (const resource of resources) { for (const resource of resources) {
nameStore.addNameToMapping(resource.resource_id, resource.name); nameStore.addNameToMapping(resource.resource_id, resource.name);
......
...@@ -92,12 +92,22 @@ function searchUsers() { ...@@ -92,12 +92,22 @@ function searchUsers() {
Search Search
</button> </button>
</form> </form>
<table class="table table-striped align-middle" v-if="userState.users"> <table
class="table table-striped align-middle caption-top"
v-if="userState.users"
>
<caption>
Displaying
{{
userState.users.length
}}
Users
</caption>
<thead> <thead>
<tr> <tr>
<th scope="col"><b>Name</b></th> <th scope="col"><b>Name</b></th>
<th scope="col">UID</th> <th scope="col">UID</th>
<th scope="col" class="text-center">Normal User</th> <th scope="col" class="text-center">Approved User</th>
<th scope="col" class="text-center">Developer</th> <th scope="col" class="text-center">Developer</th>
<th scope="col" class="text-center">Resource Maintainer</th> <th scope="col" class="text-center">Resource Maintainer</th>
<th scope="col" class="text-center">Reviewer</th> <th scope="col" class="text-center">Reviewer</th>
......
...@@ -13,12 +13,12 @@ const workflowRepository = useWorkflowStore(); ...@@ -13,12 +13,12 @@ const workflowRepository = useWorkflowStore();
const workflowsState = reactive<{ const workflowsState = reactive<{
loading: boolean; loading: boolean;
filterString: string; filterString: string;
sortByAttribute: string; sortByAttribute: "name" | "release";
sortDesc: boolean; sortDesc: boolean;
}>({ }>({
loading: true, loading: true,
filterString: "", filterString: "",
sortByAttribute: "name", sortByAttribute: "release",
sortDesc: true, sortDesc: true,
}); });
......
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