Newer
Older
<script setup lang="ts">
import { useResourceStore } from "@/stores/resources";
import { computed, onMounted, reactive } from "vue";
import {
type ResourceOut,
type ResourceVersionOut,
Status,
} from "@/client/resource";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import { Modal, Toast, Tooltip } from "bootstrap";
import ResourceVersionInfoModal from "@/components/resources/modals/ResourceVersionInfoModal.vue";
import { useNameStore } from "@/stores/names";
import BootstrapToast from "@/components/BootstrapToast.vue";
import ReasonModal from "@/components/modals/ReasonModal.vue";
const resourceRepository = useResourceStore();
const nameRepository = useNameStore();
let refreshTimeout: NodeJS.Timeout | undefined = undefined;
let rejectReasonModal: Modal | null = null;
let successToast: Toast | null = null;
let rejectToast: Toast | null = null;
const resourceState = reactive<{
sendingRequest: boolean;
loading: boolean;
inspectResource?: ResourceOut;
rejectResource?: ResourceVersionOut;
inspectVersionIndex: number;
}>({
sendingRequest: false,
loading: true,
inspectResource: undefined,
rejectResource: undefined,
inspectVersionIndex: 0,
});
const countItems = computed<number>(() =>
resourceRepository.reviewableResources.reduce(
(previousValue, currentValue) =>
previousValue + currentValue.versions.length,
0,
),
);
function fetchResources() {
resourceRepository.fetchReviewableResources(() => {
resourceState.loading = false;
});
}
function clickRefreshResources() {
clearTimeout(refreshTimeout);
refreshTimeout = setTimeout(() => {
fetchResources();
}, 500);
}
function acceptReview(resourceVersion: ResourceVersionOut) {
resourceState.sendingRequest = true;
resourceRepository
.reviewResource(resourceVersion, { deny: false })
.then(() => {
successToast?.show();
})
.finally(() => {
resourceState.sendingRequest = false;
});
function rejectReview(reason: string, resourceVersion?: ResourceVersionOut) {
if (resourceVersion) {
resourceState.sendingRequest = true;
resourceRepository
.reviewResource(resourceVersion, { deny: true, reason: reason })
.then(() => {
rejectReasonModal?.hide();
rejectToast?.show();
})
.finally(() => {
resourceState.sendingRequest = false;
});
}
onMounted(() => {
fetchResources();
new Tooltip("#refreshReviewableResourcesButton");
rejectReasonModal = new Modal("#review-reject-modal");
successToast = new Toast("#accept-resource-review-toast");
rejectToast = new Toast("#reject-resource-review-toast");
});
</script>
<template>
<bootstrap-toast
toast-id="accept-resource-review-toast"
color-class="success"
>
Accepted resource review
</bootstrap-toast>
<bootstrap-toast toast-id="reject-resource-review-toast" color-class="danger">
Rejected resource review
</bootstrap-toast>
<resource-version-info-modal
modal-id="review-resource-version-info-modal"
:resource-version-index="resourceState.inspectVersionIndex"
:resource="resourceState.inspectResource"
/>
<reason-modal
modal-id="review-reject-modal"
modal-label="Resource Review Reject Modal"
:loading="resourceState.sendingRequest"
purpose="rejection"
@save="(reason) => rejectReview(reason, resourceState.rejectResource)"
>
<template #header>
Reject Resource Review <b>{{ resourceState.rejectResource?.release }}</b>
</template>
</reason-modal>
class="row border-bottom mb-4 justify-content-between align-items-center"
<h2 class="w-fit">Resource requests</h2>
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
<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"
v-else-if="resourceRepository.reviewableResources.length > 0"
>
<caption>
Display
{{
countItems
}}
resource versions
</caption>
<thead>
<tr>
<th scope="col">Resource</th>
<th scope="col">Release</th>
<th scope="col">Status</th>
<th scope="col">Maintainer</th>
<th scope="col"></th>
<th scope="col" class="text-end">Action</th>
</tr>
</thead>
<tbody>
<template
v-for="resource in resourceRepository.reviewableResources"
:key="resource.resource_id"
>
<tr
v-for="(version, index) in resource.versions"
:key="version.resource_version_id"
>
<th>{{ resource.name }}</th>
<th>{{ version.release }}</th>
<th>{{ version.status }}</th>
<th>{{ nameRepository.getName(resource.maintainer_id) }}</th>
<th>
<button
type="button"
class="btn btn-secondary"
data-bs-toggle="modal"
data-bs-target="#review-resource-version-info-modal"
@click="
resourceState.inspectResource = resource;
resourceState.inspectVersionIndex = index;
"
>
Inspect
</button>
</th>
<th class="text-end">
<div
v-if="version.status === Status.WAIT_FOR_REVIEW"
class="btn-group"
>
<button
type="button"
class="btn btn-success btn-sm"
:disabled="resourceState.sendingRequest"
@click="acceptReview(version)"
<button
type="button"
class="btn btn-danger btn-sm"
@click="resourceState.rejectResource = version"
data-bs-toggle="modal"
data-bs-target="#review-reject-modal"
:disabled="resourceState.sendingRequest"
>
</button>
</div>
<div
v-else-if="version.status === Status.SYNCHRONIZING"
class="progress"
role="progressbar"
aria-label="Animated striped example"
aria-valuenow="100"
aria-valuemin="0"
aria-valuemax="100"
>
<div
class="progress-bar progress-bar-striped progress-bar-animated"
style="width: 100%"
></div>
</div>
</th>
</tr>
</template>
</tbody>
</table>
<div v-else class="text-center mt-5 fs-4">
There are currently no resources to review
</div>
</template>
<style scoped></style>