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

Add functionality to review resource view

#88
parent 7675fe35
No related branches found
No related tags found
1 merge request!85Resolve "Add UI for viewing and managing Resources"
...@@ -5,9 +5,12 @@ import type { ...@@ -5,9 +5,12 @@ import type {
ResourceVersionIn, ResourceVersionIn,
ResourceVersionOut, ResourceVersionOut,
} from "@/client/resource"; } from "@/client/resource";
import { ResourceService, ResourceVersionService } from "@/client/resource"; import {
ResourceService,
ResourceVersionService,
Status,
} from "@/client/resource";
import { useAuthStore } from "@/stores/users"; import { useAuthStore } from "@/stores/users";
import { Status } from "@/client/resource";
import { useNameStore } from "@/stores/names"; import { useNameStore } from "@/stores/names";
export const useResourceStore = defineStore({ export const useResourceStore = defineStore({
...@@ -16,9 +19,11 @@ export const useResourceStore = defineStore({ ...@@ -16,9 +19,11 @@ export const useResourceStore = defineStore({
({ ({
resourceMapping: {}, resourceMapping: {},
ownResourceMapping: {}, ownResourceMapping: {},
reviewableResourceMapping: {},
}) as { }) as {
resourceMapping: Record<string, ResourceOut>; resourceMapping: Record<string, ResourceOut>;
ownResourceMapping: Record<string, ResourceOut>; ownResourceMapping: Record<string, ResourceOut>;
reviewableResourceMapping: Record<string, ResourceOut>;
}, },
getters: { getters: {
resources(): ResourceOut[] { resources(): ResourceOut[] {
...@@ -27,20 +32,45 @@ export const useResourceStore = defineStore({ ...@@ -27,20 +32,45 @@ export const useResourceStore = defineStore({
ownResources(): ResourceOut[] { ownResources(): ResourceOut[] {
return Object.values(this.ownResourceMapping); return Object.values(this.ownResourceMapping);
}, },
reviewableResources(): ResourceOut[] {
return Object.values(this.reviewableResourceMapping);
},
}, },
actions: { actions: {
__addNameToMapping(key: string, value: string) {
const nameStore = useNameStore();
nameStore.addNameToMapping(key, value);
},
fetchResources( fetchResources(
maintainerId?: string, maintainerId?: string,
versionStatus?: Status[], versionStatus?: Status[],
): Promise<ResourceOut[]> { ): Promise<ResourceOut[]> {
return ResourceService.resourceListResources(maintainerId, versionStatus); return ResourceService.resourceListResources(maintainerId, versionStatus);
}, },
fetchReviewableResources(onFinally?: () => void): Promise<ResourceOut[]> {
if (Object.keys(this.reviewableResourceMapping).length > 0) {
onFinally?.();
}
return this.fetchResources(undefined, [
Status.SYNC_REQUESTED,
Status.SYNCHRONIZING,
])
.then((resources) => {
const newMapping: Record<string, ResourceOut> = {};
const nameStore = useNameStore();
for (const resource of resources) {
newMapping[resource.resource_id] = resource;
nameStore.addNameToMapping(resource.resource_id, resource.name);
for (const version of resource.versions) {
nameStore.addNameToMapping(
version.resource_version_id,
version.release,
);
}
}
this.reviewableResourceMapping = newMapping;
return resources;
})
.finally(onFinally);
},
fetchPublicResources(onFinally?: () => void): Promise<ResourceOut[]> { fetchPublicResources(onFinally?: () => void): Promise<ResourceOut[]> {
if (Object.keys(this.resourceMapping).length > 0) { if (this.resources.length > 0) {
onFinally?.(); onFinally?.();
} }
return this.fetchResources() return this.fetchResources()
...@@ -89,7 +119,7 @@ export const useResourceStore = defineStore({ ...@@ -89,7 +119,7 @@ export const useResourceStore = defineStore({
}, },
fetchOwnResources(onFinally?: () => void): Promise<ResourceOut[]> { fetchOwnResources(onFinally?: () => void): Promise<ResourceOut[]> {
const authStore = useAuthStore(); const authStore = useAuthStore();
if (Object.keys(this.ownResourceMapping).length > 0) { if (this.ownResources.length > 0) {
onFinally?.(); onFinally?.();
} }
return this.fetchResources(authStore.currentUID, Object.values(Status)) return this.fetchResources(authStore.currentUID, Object.values(Status))
...@@ -182,5 +212,36 @@ export const useResourceStore = defineStore({ ...@@ -182,5 +212,36 @@ export const useResourceStore = defineStore({
return versionOut; return versionOut;
}); });
}, },
syncResource(
resourceVersion: ResourceVersionOut,
): Promise<ResourceVersionOut> {
return ResourceVersionService.resourceVersionResourceVersionSync(
resourceVersion.resource_id,
resourceVersion.resource_version_id,
).then((changedVersion) => {
if (
this.reviewableResourceMapping[changedVersion.resource_id] ==
undefined
) {
return changedVersion;
}
const versionIndex = this.reviewableResourceMapping[
changedVersion.resource_id
].versions.findIndex(
(version) =>
version.resource_version_id == changedVersion.resource_version_id,
);
if (versionIndex > -1) {
this.reviewableResourceMapping[changedVersion.resource_id].versions[
versionIndex
] = changedVersion;
} else {
this.reviewableResourceMapping[
changedVersion.resource_id
].versions.push(changedVersion);
}
return changedVersion;
});
},
}, },
}); });
<script setup lang="ts"> <script setup lang="ts">
import { useResourceStore } from "@/stores/resources"; import { useResourceStore } from "@/stores/resources";
import { computed, onMounted, reactive } from "vue"; import { computed, onMounted, reactive } from "vue";
import { type ResourceOut, Status } from "@/client/resource"; import { type ResourceVersionOut, Status } from "@/client/resource";
import CopyToClipboardIcon from "@/components/CopyToClipboardIcon.vue"; import CopyToClipboardIcon from "@/components/CopyToClipboardIcon.vue";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import { Tooltip } from "bootstrap";
const resourceRepository = useResourceStore(); const resourceRepository = useResourceStore();
let refreshTimeout: NodeJS.Timeout | undefined = undefined;
const resourceState = reactive<{ const resourceState = reactive<{
reviewableResources: ResourceOut[]; sendingRequest: boolean;
loading: boolean; loading: boolean;
}>({ }>({
reviewableResources: [], sendingRequest: false,
loading: true, loading: true,
}); });
const countItems = computed<number>(() => const countItems = computed<number>(() =>
resourceState.reviewableResources.reduce( resourceRepository.reviewableResources.reduce(
(previousValue, currentValue) => (previousValue, currentValue) =>
previousValue + currentValue.versions.length, previousValue + currentValue.versions.length,
0, 0,
), ),
); );
function fetchResources() {
resourceRepository.fetchReviewableResources().finally(() => {
resourceState.loading = false;
});
}
function clickRefreshResources() {
clearTimeout(refreshTimeout);
refreshTimeout = setTimeout(() => {
fetchResources();
}, 500);
}
function syncResource(resourceVersion: ResourceVersionOut) {
resourceState.sendingRequest = true;
resourceRepository.syncResource(resourceVersion).finally(() => {
resourceState.sendingRequest = false;
});
}
onMounted(() => { onMounted(() => {
resourceRepository fetchResources();
.fetchResources(undefined, [Status.SYNC_REQUESTED, Status.SYNCHRONIZING]) new Tooltip("#refreshReviewableResourcesButton");
.then((resources) => {
resourceState.reviewableResources = resources;
})
.finally(() => {
resourceState.loading = false;
});
}); });
</script> </script>
<template> <template>
<div class="row m-2 border-bottom mb-4"> <div
<h2 class="mb-2">Resource Requests</h2> class="row m-2 border-bottom mb-4 justify-content-between align-items-center"
>
<h2 class="w-fit">Resource Requests</h2>
<span
class="w-fit"
tabindex="0"
data-bs-title="Refresh Reviewable Resources"
data-bs-toggle="tooltip"
id="refreshReviewableResourcesButton"
>
<button
type="button"
class="btn btn-primary btn-light me-2 shadow-sm border w-fit"
:disabled="resourceState.loading"
@click="clickRefreshResources"
>
<font-awesome-icon icon="fa-solid fa-arrow-rotate-right" />
<span class="visually-hidden">Refresh Reviewable Resources</span>
</button>
</span>
</div> </div>
<div v-if="resourceState.loading" class="text-center mt-5"> <div v-if="resourceState.loading" class="text-center mt-5">
<div class="spinner-border" style="width: 3rem; height: 3rem" role="status"> <div class="spinner-border" style="width: 3rem; height: 3rem" role="status">
<span class="visually-hidden">Loading...</span> <span class="visually-hidden">Loading...</span>
</div> </div>
</div> </div>
<table class="table caption-top table-striped table-hover align-middle"> <table
class="table caption-top table-striped table-hover align-middle"
v-else-if="resourceRepository.reviewableResources.length > 0"
>
<caption> <caption>
Display Display
{{ {{
...@@ -62,7 +101,7 @@ onMounted(() => { ...@@ -62,7 +101,7 @@ onMounted(() => {
</thead> </thead>
<tbody> <tbody>
<template <template
v-for="resource in resourceState.reviewableResources" v-for="resource in resourceRepository.reviewableResources"
:key="resource.resource_id" :key="resource.resource_id"
> >
<tr <tr
...@@ -91,10 +130,17 @@ onMounted(() => { ...@@ -91,10 +130,17 @@ onMounted(() => {
v-if="version.status === Status.SYNC_REQUESTED" v-if="version.status === Status.SYNC_REQUESTED"
class="btn-group" class="btn-group"
> >
<button type="button" class="btn btn-success btn-sm"> <button
type="button"
class="btn btn-success btn-sm"
:disabled="resourceState.sendingRequest"
@click="syncResource(version)"
>
Synchronize Synchronize
</button> </button>
<button type="button" class="btn btn-danger btn-sm">Deny</button> <button type="button" class="btn btn-danger btn-sm" disabled>
Deny
</button>
</div> </div>
<div <div
v-else-if="version.status === Status.SYNCHRONIZING" v-else-if="version.status === Status.SYNCHRONIZING"
...@@ -115,6 +161,9 @@ onMounted(() => { ...@@ -115,6 +161,9 @@ onMounted(() => {
</template> </template>
</tbody> </tbody>
</table> </table>
<div v-else>
<p>Hello</p>
</div>
</template> </template>
<style scoped></style> <style scoped></style>
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