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 {
ResourceVersionIn,
ResourceVersionOut,
} from "@/client/resource";
import { ResourceService, ResourceVersionService } from "@/client/resource";
import {
ResourceService,
ResourceVersionService,
Status,
} from "@/client/resource";
import { useAuthStore } from "@/stores/users";
import { Status } from "@/client/resource";
import { useNameStore } from "@/stores/names";
export const useResourceStore = defineStore({
......@@ -16,9 +19,11 @@ export const useResourceStore = defineStore({
({
resourceMapping: {},
ownResourceMapping: {},
reviewableResourceMapping: {},
}) as {
resourceMapping: Record<string, ResourceOut>;
ownResourceMapping: Record<string, ResourceOut>;
reviewableResourceMapping: Record<string, ResourceOut>;
},
getters: {
resources(): ResourceOut[] {
......@@ -27,20 +32,45 @@ export const useResourceStore = defineStore({
ownResources(): ResourceOut[] {
return Object.values(this.ownResourceMapping);
},
reviewableResources(): ResourceOut[] {
return Object.values(this.reviewableResourceMapping);
},
},
actions: {
__addNameToMapping(key: string, value: string) {
const nameStore = useNameStore();
nameStore.addNameToMapping(key, value);
},
fetchResources(
maintainerId?: string,
versionStatus?: Status[],
): Promise<ResourceOut[]> {
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[]> {
if (Object.keys(this.resourceMapping).length > 0) {
if (this.resources.length > 0) {
onFinally?.();
}
return this.fetchResources()
......@@ -89,7 +119,7 @@ export const useResourceStore = defineStore({
},
fetchOwnResources(onFinally?: () => void): Promise<ResourceOut[]> {
const authStore = useAuthStore();
if (Object.keys(this.ownResourceMapping).length > 0) {
if (this.ownResources.length > 0) {
onFinally?.();
}
return this.fetchResources(authStore.currentUID, Object.values(Status))
......@@ -182,5 +212,36 @@ export const useResourceStore = defineStore({
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">
import { useResourceStore } from "@/stores/resources";
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 FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import { Tooltip } from "bootstrap";
const resourceRepository = useResourceStore();
let refreshTimeout: NodeJS.Timeout | undefined = undefined;
const resourceState = reactive<{
reviewableResources: ResourceOut[];
sendingRequest: boolean;
loading: boolean;
}>({
reviewableResources: [],
sendingRequest: false,
loading: true,
});
const countItems = computed<number>(() =>
resourceState.reviewableResources.reduce(
resourceRepository.reviewableResources.reduce(
(previousValue, currentValue) =>
previousValue + currentValue.versions.length,
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(() => {
resourceRepository
.fetchResources(undefined, [Status.SYNC_REQUESTED, Status.SYNCHRONIZING])
.then((resources) => {
resourceState.reviewableResources = resources;
})
.finally(() => {
resourceState.loading = false;
});
fetchResources();
new Tooltip("#refreshReviewableResourcesButton");
});
</script>
<template>
<div class="row m-2 border-bottom mb-4">
<h2 class="mb-2">Resource Requests</h2>
<div
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 v-if="resourceState.loading" class="text-center mt-5">
<div class="spinner-border" style="width: 3rem; height: 3rem" role="status">
<span class="visually-hidden">Loading...</span>
</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>
Display
{{
......@@ -62,7 +101,7 @@ onMounted(() => {
</thead>
<tbody>
<template
v-for="resource in resourceState.reviewableResources"
v-for="resource in resourceRepository.reviewableResources"
:key="resource.resource_id"
>
<tr
......@@ -91,10 +130,17 @@ onMounted(() => {
v-if="version.status === Status.SYNC_REQUESTED"
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
</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
v-else-if="version.status === Status.SYNCHRONIZING"
......@@ -115,6 +161,9 @@ onMounted(() => {
</template>
</tbody>
</table>
<div v-else>
<p>Hello</p>
</div>
</template>
<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