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"
This commit is part of merge request !85. Comments created here will be created in the context of that merge request.
......@@ -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