Skip to content
This repository was archived by the owner on Feb 17, 2025. It is now read-only.

Commit

Permalink
Merge pull request #267 from BBMRI-ERIC/feat/override_resources
Browse files Browse the repository at this point in the history
feat: add modal for editing resources
  • Loading branch information
RadovanTomik authored Sep 2, 2024
2 parents 0212738 + 7c9cf73 commit 951fbf3
Show file tree
Hide file tree
Showing 11 changed files with 1,005 additions and 116 deletions.
2 changes: 2 additions & 0 deletions components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
AccessCriteriaSection: typeof import('./src/components/negotiation-tabs/AccessCriteriaSection.vue')['default']
AddResourcesButton: typeof import('./src/components/AddResourcesButton.vue')['default']
AddResourcesModal: typeof import('./src/components/modals/AddResourcesModal.vue')['default']
Alert: typeof import('./src/components/Alert.vue')['default']
BAvatar: typeof import('bootstrap-vue-next')['BAvatar']
ConfirmationModal: typeof import('./src/components/modals/ConfirmationModal.vue')['default']
Expand Down
3 changes: 2 additions & 1 deletion oidc_mock/config/clients.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"ClientSecrets": ["authorization-code-with-pkce-client-secret"],
"AllowedGrantTypes": ["authorization_code"],
"AllowAccessTokensViaBrowser": true,
"RedirectUris": ["http://localhost:8080/logged-in", "http://localhost:8081/api/swagger-ui/oauth2-redirect.html"],
"RedirectUris": ["http://localhost:8080/logged-in", "http://localhost:8081/api/swagger-ui/oauth2-redirect.html", "http://localhost:8080"],
"AllowedScopes": ["openid", "profile", "email", "permissions", "some-app-scope-1"],
"PostLogoutRedirectUris": ["http://localhost:8080"],
"IdentityTokenLifetime": 36000,
"AccessTokenLifetime": 3600,
"ClientClaimsPrefix": ""
Expand Down
45 changes: 45 additions & 0 deletions src/components/AddResourcesButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<template>
<button
type="button"
class="btn"
@click="openModal()"
>
<i class="bi bi-pencil-square" />
Edit
</button>
<add-resources-modal
id="resourcesModal"
:shown="shown"
:negotiation-id="props.negotiationId"
@confirm="closeModal()"
/>
</template>
<script setup>
import AddResourcesModal from "@/components/modals/AddResourcesModal.vue"
import { Modal } from "bootstrap"
import { ref } from "vue"
const shown = ref(false)
const resourceModal = ref(undefined)
const props = defineProps({
negotiationId: {
type: String,
required: true
}
})
const emit = defineEmits(["new-resources"])
function openModal () {
resourceModal.value = new Modal(document.querySelector("#resourcesModal"))
shown.value = true
resourceModal.value.show()
}
function closeModal () {
emit("new-resources")
resourceModal.value.hide()
}
</script>

<style scoped>
.btn:hover {
color: #e95713;
}
</style>
2 changes: 2 additions & 0 deletions src/components/NegotiationPosts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ async function addAttachmentToNegotiation () {
console.log(`Successfully uploaded file: ${attachment.value.name}`)
}
})
emit("new_attachment")
}
function resetForm () {
Expand Down Expand Up @@ -320,6 +321,7 @@ function getFileTypeName (fileType) {
return "DOC"
}
}
const emit = defineEmits(["new_attachment"])
</script>

<style scoped>
Expand Down
279 changes: 279 additions & 0 deletions src/components/modals/AddResourcesModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
<template>
<div
class="modal"
:class="{ fade: fade }"
tabindex="-1"
:aria-labelledby="`${id}Label`"
aria-hidden="true"
>
<div
class="modal-dialog modal-dialog-centered modal-xl"
>
<div class="modal-content">
<div class="modal-header">
<h4 class="justify-content-center">
Edit Resources
</h4>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
/>
</div>
<div class="modal-body text-left">
<h5 class="text-center mb-3">
Select Resources and their desired Status
</h5>
<div class="input-group flex-nowrap">
<input
v-model="searchQuery"
type="text"
class="form-control mb-3"
placeholder="Search by name..."
aria-label="Search"
aria-describedby="addon-wrapping"
@input="onSearch"
>
</div>
<div class="d-flex justify-content-between">
<div class="ms-3 text-muted">
Found Resources: {{ getNumberOfFoundResources() }}
</div>
<div class="d-flex justify-content-end mb-2">
Selected Resources: {{ getNumberOfSelectedResources() }}
</div>
</div>
<div class="d-flex flex-row justify-content-end mb-2">
Status:
<div class="col-3 ms-2">
<select
v-model="selectedState"
class="form-select form-select-sm btn-outline-sort-filter-button-outline"
>
<option
disabled
value=""
>
Select a status...
</option>
<option
v-for="(state, index) in states"
:key="index"
:value="state"
>
{{ state.label }}
</option>
</select>
</div>
</div>
<!-- Loading Spinner -->
<div
v-if="loading"
class="text-center my-5"
>
<div
class="spinner-border"
role="status"
>
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div v-else>
<div class="mb-3 d-flex justify-content-between">
<div>
<input
v-model="selectAll"
type="checkbox"
class="form-check-input"
@change="toggleSelectAll"
>
<label class="form-check-label ms-2">Select All</label>
</div>
<button
class="btn btn-primary mx-2"
@click="addResources"
>
Add
</button>
</div>
<table class="table table-sm">
<thead>
<tr>
<th scope="col">
Select
</th>
<th scope="col">
Resource Name
</th>
<th scope="col">
Resource ID
</th>
</tr>
</thead>
<tbody>
<tr
v-for="collection in resources"
:key="collection.id"
>
<td>
<input
:id="collection.id"
v-model="selectedResources"
type="checkbox"
:value="collection.id"
class="form-check-input"
@change="handleCheckboxChange"
>
</td>
<td>
<label
:for="collection.id"
class="form-check-label"
>{{ collection.name }}</label>
</td>
<td>
<label
:for="collection.id"
class="form-check-label"
>{{ collection.sourceId }}</label>
</td>
</tr>
</tbody>
</table>
<!-- Pagination Controls -->
<div class="d-flex justify-content-center my-4">
<button
class="btn btn-secondary me-2"
:disabled="pageNumber === 0"
@click="fetchPage(pageLinks.previous.href)"
>
Previous
</button>
<span class="mx-2">Page {{ pageNumber + 1 }} of {{ totalPages }}</span>
<button
class="btn btn-secondary ms-2"
:disabled="pageNumber === totalPages - 1"
@click="fetchPage(pageLinks.next.href)"
>
Next
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, watch } from "vue"
import { Tooltip } from "bootstrap"
import debounce from "@popperjs/core/lib/utils/debounce"
import { useNegotiationPageStore } from "@/store/negotiationPage"
const resources = ref([])
const selectedResources = ref([])
const selectAll = ref(false)
const loading = ref(true)
const pageNumber = ref(0)
const totalPages = ref(0)
const totalElements = ref(0)
const pageLinks = ref({})
const searchQuery = ref("")
const states = ref([])
const selectedState = ref({})
const props = defineProps({
shown: {
type: Boolean,
required: true
},
negotiationId: {
type: String,
required: true
}
})
const store = useNegotiationPageStore()
watch(() => props.shown, (first, second) => {
if (props.shown !== false) {
loadResources()
loadStates()
}
})
watch(selectedResources, (newVal) => {
selectAll.value = newVal.length === resources.value.length
})
onMounted(() => {
new Tooltip(document.body, {
selector: "[data-bs-toggle='tooltip']"
})
})
async function loadResources (name = "") {
const response = await store.retrieveAllResources(name)
resources.value = response?._embedded?.resources ?? []
pageLinks.value = response._links
pageNumber.value = response.page.number
totalPages.value = response.page.totalPages
totalElements.value = response.page.totalElements
loading.value = false
}
async function loadStates () {
const response = await store.retrieveResourceAllStates()
states.value = response._embedded.states
}
const emit = defineEmits(["confirm"])
async function addResources () {
let data = { resourceIds: selectedResources.value }
if (selectedState.value) {
data = {
resourceIds: selectedResources.value,
state: selectedState.value.value
}
}
const negotiationId = props.negotiationId
await store.addResources(data, negotiationId)
selectedResources.value = []
emit("confirm")
}
const toggleSelectAll = () => {
if (selectAll.value) {
// Select all resources
selectedResources.value = resources.value.map(resource => resource.id)
} else {
// Deselect all resources
selectedResources.value = []
}
}
const handleCheckboxChange = () => {
// If not all resources are selected, uncheck the Select All checkbox
if (selectedResources.value.length !== resources.value.length) {
selectAll.value = false
}
}
async function fetchPage (url) {
const response = await store.dispatch("fetchURL", { url })
resources.value = response._embedded.resources
pageLinks.value = response._links
pageNumber.value = response.page.number
}
// Method to handle search input
const onSearch = debounce(async () => {
if (searchQuery.value.length >= 3) {
loading.value = true
await loadResources(searchQuery.value)
}
if (searchQuery.value.length === 0) {
loading.value = true
await loadResources(searchQuery.value)
}
}, 1000) // Debounce delay in milliseconds
function getNumberOfSelectedResources () {
return selectedResources.value.length
}
function getNumberOfFoundResources () {
return totalElements.value
}
</script>
<style scoped>
</style>
Loading

0 comments on commit 951fbf3

Please sign in to comment.