Skip to content

Commit

Permalink
Coommit includes the following: (#634)
Browse files Browse the repository at this point in the history
- Titles of FASCA and Drone in all Trainings #616
- User Profile > Save Changes Without Returning the User to the Search Results Screen #621
- Bug: Admin User unable to download another user's certificate #623
- Dependabot Alert: Server-Side Request Forgery in axios #620
  • Loading branch information
john-labbate authored Sep 6, 2024
1 parent 8935f7b commit 37afe3d
Show file tree
Hide file tree
Showing 17 changed files with 299 additions and 131 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ __pycache__
_site/
node_modules
.coverage
.idea/workspace.xml
18 changes: 15 additions & 3 deletions training-front-end/src/components/AdminEditReporting.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,19 @@ const editing = ref(false)
// Copy to avoid modifying parent prop and allow cancelling edits
const agencies = ref([...props.user.report_agencies])
const emit = defineEmits(['cancel', 'save', 'userUpdateSuccess'])
watch(
() => props.user,
(updatedUser) => {
if (updatedUser && updatedUser.report_agencies) {
agencies.value = [...updatedUser.report_agencies];
} else {
agencies.value = [];
}
},
{ deep: true } // Ensures that nested changes within the user object are detected
);
const emit = defineEmits(['cancel', 'updateReportingAccess', 'userUpdateSuccess'])
const agency_options = useStore(agencyList)
const bureaus = useStore(bureauList)
Expand All @@ -41,7 +53,7 @@ function editUserAgencies(e, checked) {
})
} else {
agencies.value = agencies.value.filter(agency => agency.id != e.id)
emit('save', props.user.id, agencies.value)
emit('updateReportingAccess', props.user.id, agencies.value)
}
}
Expand Down Expand Up @@ -268,7 +280,7 @@ function formatDate(dateStr) {
<button
id="update-user"
class="usa-button usa-button--outline"
@click="$emit('save', user.id, agencies)"
@click="$emit('updateReportingAccess', user.id, agencies)"
>
Add Reporting Access
</button>
Expand Down
66 changes: 17 additions & 49 deletions training-front-end/src/components/AdminEditUserDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import {useVuelidate} from "@vuelidate/core";
import ValidatedSelect from "./ValidatedSelect.vue";
import {useStore} from "@nanostores/vue";
import {agencyList, bureauList, setSelectedAgencyId} from "../stores/agencies.js";
import {profile} from "../stores/user.js";
import USWDSAlert from "./USWDSAlert.vue";
import SpinnerGraphic from "./SpinnerGraphic.vue";
import { RepositoryFactory } from "./RepositoryFactory.vue";
const adminRepository = RepositoryFactory.get('admin')
const props = defineProps({
userToEdit: {
type: Object,
required: true,
}
})
const user = useStore(profile)
const base_url = import.meta.env.PUBLIC_API_BASE_URL
const {withMessage} = helpers
const agency_options = useStore(agencyList)
const bureaus = useStore(bureauList)
Expand Down Expand Up @@ -62,8 +62,7 @@ onBeforeMount(async () => {
})
async function update_user_info() {
error.value = ref()
show_error.value = false
clearErrors()
const isFormValid = await v_all_info$.value.$validate()
if (!isFormValid) {
Expand All @@ -72,60 +71,29 @@ async function update_user_info() {
is_saving.value = true
show_spinner.value = true
// When user has choosen a bureau use that id instead of the agency
// When user has chosen a bureau use that id instead of the agency
let {bureau_id, ...user_data} = user_input
if (bureau_id) {
user_data.agency_id = bureau_id
}
const apiURL = new URL(`${base_url}/api/v1/users/${props.userToEdit.id}`)
let response = ref();
try {
response.value = await fetch(apiURL, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${user.value.jwt}`
},
body: JSON.stringify(user_data)
})
} catch (error) {
setError({
name: 'Server Error',
message: 'Sorry, we had an error connecting to the server.'
})
try{
let updatedUser = await adminRepository.updateUser(props.userToEdit.id, user_data)
is_saving.value = false
show_spinner.value = false
return
}
if (!response.value.ok) {
is_saving.value = false
show_spinner.value = false
if (response.value.status === 400) {
setError({
name: 'Unauthorized',
message: 'You are not authorized to edit profile.'
})
return
}
if (response.value.status === 403) {
setError({
name: 'Unauthorized',
message: "You can not update your own profile."
})
return
}
let successMessage = `Successfully updated ${updatedUser.email}`
emit('completeUserUpdate', successMessage)
} catch(err){
setError({
name: 'Error',
message: "Error contacting server."
name: 'Response Error',
message: err
})
return
}
is_saving.value = false
show_spinner.value = false
let updatedUser = await response.value.json()
let successMessage = `Successfully updated ${updatedUser.email}`
emit('completeUserUpdate', successMessage)
}
function clearErrors(){
error.value = ref()
show_error.value = false
}
function setError(event) {
Expand Down
106 changes: 106 additions & 0 deletions training-front-end/src/components/AdminRepository.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<script>
import { profile} from '../stores/user'
import { useStore } from '@nanostores/vue'
import {ref} from "vue";
const user = useStore(profile)
const base_url = import.meta.env.PUBLIC_API_BASE_URL
const users_api = `${base_url}/api/v1/users/`
const userSearch = async function(searchText, currentPage){
const url = new URL(`${users_api}`)
url.search = new URLSearchParams({searchText: searchText, page_number: currentPage + 1})
const response = await fetch(
url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${user.value.jwt}`
}
}
)
if (! response.ok) {
const message = await response.text()
throw new Error(message)
}
return await response.json()
}
const updateUserReports = async function(userId, agencyIds) {
const agencies = agencyIds.map(a => a.id)
const url = new URL(`${users_api}edit-user-for-reporting/`)
url.search = new URLSearchParams({user_id: userId})
const response = await fetch(
url, {
method: "PATCH",
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${user.value.jwt}`
},
body: JSON.stringify(agencies)
}
)
if (!response.ok) {
const message = await response.text()
throw new Error(message)
}
return await response.json()
}
const getUser = async function(userId){
const url = new URL(`${users_api}${userId}`)
const response = await fetch(
url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${user.value.jwt}`
}
}
)
if (! response.ok) {
const message = await response.text()
throw new Error(message)
}
return await response.json()
}
const updateUser = async function(userId, userData){
const apiURL = new URL(`${users_api}${userId}`)
let response = ref();
try {
response.value = await fetch(apiURL, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${user.value.jwt}`
},
body: JSON.stringify(userData)
})
} catch (error) {
throw new Error('Sorry, we had an error connecting to the server.')
}
if (!response.value.ok) {
if (response.value.status === 400) {
throw new Error('You are not authorized to edit profile.')
}
if (response.value.status === 403) {
throw new Error("You can not update your own profile.")
}
throw new Error("Error contacting server.")
}
return await response.value.json()
}
export default {
userSearch,
updateUserReports,
getUser,
updateUser,
}
</script>
76 changes: 23 additions & 53 deletions training-front-end/src/components/AdminSearchUser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@
import USWDSPagination from "./USWDSPagination.vue";
import USWDSAlert from './USWDSAlert.vue'
import { setSelectedAgencyId} from '../stores/agencies'
import { useStore } from '@nanostores/vue'
import { profile} from '../stores/user'
const user = useStore(profile)
import { RepositoryFactory } from "./RepositoryFactory.vue";
const adminRepository = RepositoryFactory.get('admin')
const PAGE_SIZE = 25
const base_url = import.meta.env.PUBLIC_API_BASE_URL
const report_url = `${base_url}/api/v1/users/`
const update_url = `${base_url}/api/v1/users/edit-user-for-reporting/`
let currentPage = ref(0)
let numberOfResults = ref(0)
const numberOfPages = computed(() => Math.ceil(numberOfResults.value/PAGE_SIZE))
Expand All @@ -36,23 +30,8 @@
async function search() {
clearAlerts()
noResults.value = false
const url = new URL(`${report_url}`)
url.search = new URLSearchParams({searchText: searchTerm.value, page_number: currentPage.value + 1})
try {
const response = await fetch(
url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${user.value.jwt}`
}
}
)
if (! response.ok) {
const message = await response.text()
throw new Error(message)
}
let search_results = await response.json()
let search_results = await adminRepository.userSearch(searchTerm.value, currentPage.value)
searchResults.value = search_results.users
numberOfResults.value = search_results.total_count
noResults.value = search_results.total_count === 0
Expand All @@ -62,50 +41,41 @@
}
async function updateUserReports(userId, agencyIds) {
const agencies = agencyIds.map(a => a.id)
const url = new URL(update_url)
url.search = new URLSearchParams({user_id: userId})
try {
const response = await fetch(
url, {
method: "PATCH",
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${user.value.jwt}`
},
body: JSON.stringify(agencies)
}
)
if (!response.ok) {
const message = await response.text()
throw new Error(message)
}
let updatedUser = await response.json()
let updatedUser = await adminRepository.updateUserReports(userId, agencyIds)
selectedUser.value.report_agencies = updatedUser.report_agencies
setCurrentUser(undefined)
setSelectedAgencyId(undefined)
updateUserSuccess("Updated users reporting access")
refreshSelectedUser()
} catch (err){
error.value = err
}
}
function setCurrentUser(e) {
async function refreshSelectedUser(){
if(!selectedUser.value){
return
}
try{
selectedUser.value = await adminRepository.getUser(selectedUser.value.id)
} catch (err) {
error.value = err
}
}
function setSelectedUser(e) {
selectedUser.value = e
}
function cancelEdit(){
setCurrentUser(undefined)
setSelectedUser(undefined)
setSelectedAgencyId(undefined)
}
async function updateUserSuccess(message) {
refreshSelectedUser()
successMessage.value = message
showSuccessMessage.value = true
cancelEdit()
currentPage.value = 0
numberOfResults.value = 0
searchTerm.value = ''
searchResults.value = []
}
function clearAlerts() {
Expand Down Expand Up @@ -182,7 +152,7 @@
<AdminUserSearchTable
:number-of-results="numberOfResults"
:search-results="searchResults"
@select-item="setCurrentUser"
@select-item="setSelectedUser"
/>
<USWDSPagination
:current-page="currentPage"
Expand All @@ -200,7 +170,7 @@
<div v-else>
<AdminEditReporting
:user="selectedUser"
@save="updateUserReports"
@update-reporting-access="updateUserReports"
@cancel="cancelEdit"
@user-update-success="updateUserSuccess"
/>
Expand Down
Loading

0 comments on commit 37afe3d

Please sign in to comment.