Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1981 - Request Offering Change: Ministry View/Approve/Deny (UI only) - Part 1 #2363

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Controller, Get, Param, ParseIntPipe, Query } from "@nestjs/common";
import {
Body,
Controller,
Get,
Param,
ParseIntPipe,
Patch,
Query,
} from "@nestjs/common";
import { ApiNotFoundResponse, ApiTags } from "@nestjs/swagger";
import { AuthorizedParties } from "../../auth/authorized-parties.enum";
import { AllowAuthorizedParty, Groups } from "../../auth/decorators";
Expand All @@ -9,6 +17,7 @@ import {
AllInProgressOfferingChangePaginationOptionsAPIInDTO,
AllInProgressApplicationOfferingChangesAPIOutDTO,
ApplicationOfferingChangeDetailsAPIOutDTO,
ApplicationOfferingChangeAssessmentAPIInDTO,
} from "./models/application-offering-change-request.dto";
import { ApplicationOfferingChangeRequestService } from "../../services";
import { ApplicationOfferingChangeRequestStatus } from "@sims/sims-db";
Expand Down Expand Up @@ -91,4 +100,19 @@ export class ApplicationOfferingChangeRequestAESTController extends BaseControll
{ hasAuditAndNoteDetails: true },
);
}

/**
* Approves or declines an application offering change request.
* @param applicationOfferingChangeRequestId application offering change request id.
* @param payload information to update the application offering change request.
*/
@Patch(":applicationOfferingChangeRequestId")
dheepak-aot marked this conversation as resolved.
Show resolved Hide resolved
async updateApplicationOfferingChangeRequest(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the the proper role for the method.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be a part of the next PR. I have updated the TODO comment with the same.

@Param("applicationOfferingChangeRequestId", ParseIntPipe)
applicationOfferingChangeRequestId: number,
@Body()
payload: ApplicationOfferingChangeAssessmentAPIInDTO,
): Promise<void> {
// TODO: API including the addition of ministry role to be done in the next PR.
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ApplicationOfferingChangeRequestStatus,
NOTE_DESCRIPTION_MAX_LENGTH,
OfferingIntensity,
REASON_MAX_LENGTH,
} from "@sims/sims-db";
Expand Down Expand Up @@ -173,6 +174,17 @@ export class StudentApplicationOfferingChangeRequestAPIInDTO {
applicationOfferingChangeRequestStatus: ApplicationOfferingChangeRequestStatus;
}

export class ApplicationOfferingChangeAssessmentAPIInDTO {
@IsNotEmpty()
@MaxLength(NOTE_DESCRIPTION_MAX_LENGTH)
note: string;
@IsIn([
ApplicationOfferingChangeRequestStatus.Approved,
ApplicationOfferingChangeRequestStatus.DeclinedBySABC,
])
applicationOfferingChangeRequestStatus: ApplicationOfferingChangeRequestStatus;
}

/**
* All in progress application offering change details.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<template>
<v-form ref="assessApplicationOfferingChangeRequestForm">
<modal-dialog-base :showDialog="showDialog" :title="title">
<template #content
><error-summary
:errors="assessApplicationOfferingChangeRequestForm.errors"
/>
<p class="my-4">
andrewsignori-aot marked this conversation as resolved.
Show resolved Hide resolved
{{ subject }}
</p>
<v-textarea
label="Notes"
variant="outlined"
hide-details="auto"
v-model="note"
:rules="[checkNotesLengthRule]"
required
/>
<p class="pt-1 brand-gray-text">
Notes will be visible to StudentAid staff and institutions. This will
not be shown to students.
</p>
</template>
<template #footer>
<footer-buttons
:primaryLabel="primaryLabel"
@secondaryClick="cancel"
@primaryClick="assessChange"
/>
</template>
</modal-dialog-base>
</v-form>
</template>
<script lang="ts">
import ModalDialogBase from "@/components/generic/ModalDialogBase.vue";
import { ref, defineComponent, computed } from "vue";
import { useModalDialog, useRules } from "@/composables";
import { ApplicationOfferingChangeAssessmentAPIInDTO } from "@/services/http/dto";
import { ApplicationOfferingChangeRequestStatus, VForm } from "@/types";
export default defineComponent({
components: {
ModalDialogBase,
Copy link
Collaborator

@andrewsignori-aot andrewsignori-aot Sep 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that we talked in the past about having a common "Approval" modal dialog (similar to the ConfimrModal) that would allow adding a note and a decline/approve buttons but we never went for that. This situation would be a perfect example of using a generic approval modal.

},
setup() {
const note = ref("");
const { showDialog, showModal, resolvePromise, showParameter } =
useModalDialog<ApplicationOfferingChangeAssessmentAPIInDTO | boolean>();
const assessApplicationOfferingChangeRequestModal =
{} as ApplicationOfferingChangeAssessmentAPIInDTO;
const assessApplicationOfferingChangeRequestForm = ref({} as VForm);
const { checkNotesLengthRule } = useRules();
const title = computed(() =>
showParameter.value ===
ApplicationOfferingChangeRequestStatus.DeclinedBySABC
? "Decline for reassessment"
: "Approve for reassessment",
);
const subject = computed(() =>
showParameter.value === ApplicationOfferingChangeRequestStatus.Approved
? "Outline the reasoning for approving this request. Please add the application number."
: "Outline the reasoning for declining this request. Please add the application number.",
);
const primaryLabel = computed(() =>
showParameter.value ===
ApplicationOfferingChangeRequestStatus.DeclinedBySABC
? "Decline now"
: "Approve now",
);
const cancel = () => {
assessApplicationOfferingChangeRequestForm.value.reset();
assessApplicationOfferingChangeRequestForm.value.resetValidation();
resolvePromise(false);
};
const assessChange = async () => {
const validationResult =
await assessApplicationOfferingChangeRequestForm.value.validate();
if (!validationResult.valid) {
return;
}
assessApplicationOfferingChangeRequestModal.applicationOfferingChangeRequestStatus =
showParameter.value;
assessApplicationOfferingChangeRequestModal.note = note.value;
resolvePromise(assessApplicationOfferingChangeRequestModal);
assessApplicationOfferingChangeRequestForm.value.reset();
};
return {
showDialog,
showModal,
showParameter,
cancel,
assessChange,
note,
title,
subject,
primaryLabel,
checkNotesLengthRule,
ApplicationOfferingChangeRequestStatus,
assessApplicationOfferingChangeRequestForm,
};
},
});
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
>
<v-card-title>
<slot name="header">
<h2 v-if="title" class="category-header-large primary-color">
<h2 v-if="title" class="category-header-large primary-color mt-3">
{{ title }}
</h2>
</slot>
</v-card-title>
<v-divider class="border-opacity-100" :thickness="2" inset />
<v-divider class="border-opacity-100 my-1" :thickness="2" inset />
<v-card-text class="pt-0">
<div class="pb-2" v-if="subTitle">{{ subTitle }}</div>
<slot name="content">Please add the modal content here!</slot>
</v-card-text>
<v-divider class="border-opacity-100" :thickness="2" inset />
<v-divider class="border-opacity-100 my-1" :thickness="2" inset />
dheepak-aot marked this conversation as resolved.
Show resolved Hide resolved

<v-card-actions>
<v-spacer></v-spacer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ApplicationOfferingDetailsAPIOutDTO,
ApplicationOfferingChangeRequestStatusAPIOutDTO,
ApplicationOfferingChangeDetailsAPIOutDTO,
ApplicationOfferingChangeAssessmentAPIInDTO,
} from "@/services/http/dto";

export class ApplicationOfferingChangeRequestService {
Expand Down Expand Up @@ -197,4 +198,19 @@ export class ApplicationOfferingChangeRequestService {
payload,
);
}

/**
* Approves or declines the application offering change request status.
* @param applicationOfferingChangeRequestId application offering change request id.
* @param payload information to update the application offering change request.
*/
async assessApplicationOfferingChangeRequest(
applicationOfferingChangeRequestId: number,
payload: ApplicationOfferingChangeAssessmentAPIInDTO,
): Promise<void> {
await ApiClient.ApplicationOfferingChangeRequestApi.assessApplicationOfferingChangeRequest(
applicationOfferingChangeRequestId,
payload,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ApplicationOfferingDetailsAPIOutDTO,
ApplicationOfferingChangeRequestStatusAPIOutDTO,
ApplicationOfferingChangeDetailsAPIOutDTO,
ApplicationOfferingChangeAssessmentAPIInDTO,
} from "@/services/http/dto";
import { getPaginationQueryString } from "@/helpers";

Expand Down Expand Up @@ -197,4 +198,17 @@ export class ApplicationOfferingChangeRequestApi extends HttpBaseClient {
const url = `application-offering-change-request/${applicationOfferingChangeRequestId}`;
await this.patchCall(this.addClientRoot(url), payload);
}

/**
* Approve or decline the application offering change request status.
* @param applicationOfferingChangeRequestId application offering change request id.
* @param payload information to update the application offering change request status and save the declaration.
*/
async assessApplicationOfferingChangeRequest(
applicationOfferingChangeRequestId: number,
payload: ApplicationOfferingChangeAssessmentAPIInDTO,
): Promise<void> {
const url = `application-offering-change-request/${applicationOfferingChangeRequestId}`;
await this.patchCall(this.addClientRoot(url), payload);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,8 @@ export interface StudentApplicationOfferingChangeRequestAPIInDTO {
studentConsent: boolean;
applicationOfferingChangeRequestStatus: ApplicationOfferingChangeRequestStatus;
}

export interface ApplicationOfferingChangeAssessmentAPIInDTO {
note: string;
applicationOfferingChangeRequestStatus: ApplicationOfferingChangeRequestStatus;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,26 @@
ApplicationOfferingChangeRequestStatus.InProgressWithSABC
"
>
<v-btn color="primary" variant="outlined">Decline reassessment</v-btn>
<v-btn class="ml-2" color="primary">Approve reassessment</v-btn>
<v-btn
color="primary"
variant="outlined"
@click="
assessApplicationOfferingChangeRequest(
ApplicationOfferingChangeRequestStatus.DeclinedBySABC,
)
"
>Decline reassessment</v-btn
>
<v-btn
class="ml-2"
color="primary"
@click="
assessApplicationOfferingChangeRequest(
ApplicationOfferingChangeRequestStatus.Approved,
)
"
>Approve reassessment</v-btn
>
</template>
</header-navigator>
<application-offering-change-details-header
Expand Down Expand Up @@ -58,32 +76,39 @@
/>
</v-window-item>
</v-window>
<assess-offering-change-modal ref="assessOfferingChangeModal" />
<assess-application-offering-change-request-modal
ref="assessApplicationOfferingChangeRequestModal"
/>
</full-page-container>
</template>

<script lang="ts">
import { ref, defineComponent, computed, onMounted } from "vue";
import { RouteLocationRaw } from "vue-router";
import { useRouter, RouteLocationRaw } from "vue-router";
import {
ApplicationOfferingChangeRequestHeader,
ApplicationOfferingChangeRequestStatus,
} from "@/types";
import { ApplicationOfferingChangeDetailsAPIOutDTO } from "@/services/http/dto";
import {
ApplicationOfferingChangeAssessmentAPIInDTO,
ApplicationOfferingChangeDetailsAPIOutDTO,
} from "@/services/http/dto";
import { AESTRoutesConst } from "@/constants/routes/RouteConstants";
import ApplicationOfferingChangeDetailsHeader from "@/components/aest/institution/ApplicationOfferingChangeDetailsHeader.vue";
import { ApplicationOfferingChangeRequestService } from "@/services/ApplicationOfferingChangeRequestService";
import StudentApplicationOfferingChangeDetails from "@/components/aest/StudentApplicationOfferingChangeDetails.vue";
import AssessApplicationOfferingChangeRequestModal from "@/components/aest/AssessApplicationOfferingChangeRequestModal.vue";
import OfferingForm from "@/components/common/OfferingForm.vue";
import { BannerTypes } from "@/types/contracts/Banner";
import { ApplicationOfferingDetails } from "@/types/contracts/StudentApplicationOfferingChangeContract";
import { useFormatters } from "@/composables";
import { ModalDialog, useFormatters, useSnackBar } from "@/composables";
export default defineComponent({
components: {
StudentApplicationOfferingChangeDetails,
ApplicationOfferingChangeDetailsHeader,
OfferingForm,
AssessApplicationOfferingChangeRequestModal,
},
props: {
studentId: {
Expand All @@ -109,6 +134,11 @@ export default defineComponent({
const studentApplicationOfferingDetails = ref(
{} as ApplicationOfferingDetails,
);
const assessApplicationOfferingChangeRequestModal = ref(
{} as ModalDialog<ApplicationOfferingChangeAssessmentAPIInDTO | boolean>,
);
const snackBar = useSnackBar();
const router = useRouter();
onMounted(async () => {
applicationOfferingChangeRequestDetails.value =
await ApplicationOfferingChangeRequestService.shared.getApplicationOfferingDetailsForReview(
Expand Down Expand Up @@ -147,6 +177,33 @@ export default defineComponent({
),
};
});
const assessApplicationOfferingChangeRequest = async (
applicationOfferingChangeRequestStatus: ApplicationOfferingChangeRequestStatus,
) => {
const responseData =
await assessApplicationOfferingChangeRequestModal.value.showModal(
applicationOfferingChangeRequestStatus,
);
if (responseData) {
try {
await ApplicationOfferingChangeRequestService.shared.assessApplicationOfferingChangeRequest(
props.applicationOfferingChangeRequestId,
responseData as ApplicationOfferingChangeAssessmentAPIInDTO,
);
router.push({
name: AESTRoutesConst.ASSESSMENTS_SUMMARY,
});
snackBar.success(
"Your decision was submitted. You can refer to the outcome below.",
);
} catch {
snackBar.error(
"Unexpected error while submitting application offering change request.",
);
}
}
};
const goBackRouteParams = computed(
() =>
({
Expand All @@ -166,6 +223,8 @@ export default defineComponent({
goBackRouteParams,
studentApplicationOfferingDetails,
applicationOfferingChangeRequestDetails,
assessApplicationOfferingChangeRequest,
assessApplicationOfferingChangeRequestModal,
};
},
});
Expand Down