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

SS-764: Setup bulk unassign functionality in manage duties screen #237

Merged
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
10 changes: 5 additions & 5 deletions docker/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ services:

api:
image: ss-api
environment:
environment:
- LocationServicesClient__Username=${LocationServicesClientUsername}
- LocationServicesClient__Password=${LocationServicesClientPassword}
- LocationServicesClient__Url=${LocationServicesClientUrl}
Expand All @@ -36,8 +36,8 @@ services:
- ASPNETCORE_URLS=${ASPNETCORE_URLS}
- WebBaseHref=${WEB_BASE_HREF}
- SiteMinderLogoutUrl=${SiteMinderLogoutUrl}
- Logging__LogLevel__Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker=${ControllerLoggingLevel}
- Logging__LogLevel__SS.Api.infrastructure.middleware.ErrorHandlingMiddleware=${MiddlewareLoggingLevel}
- Logging__LogLevel__Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker=${ControllerLoggingLevel}
- Logging__LogLevel__SS.Api.infrastructure.middleware.ErrorHandlingMiddleware=${MiddlewareLoggingLevel}
- Logging__LogLevel__Microsoft.EntityFrameworkCore.Database.Command=${DatabaseLoggingLevel}
- ASPNETCORE_Kestrel__Certificates__Default__Password=${KestrelPassword}
- ASPNETCORE_Kestrel__Certificates__Default__Path=${KestrelPath}
Expand Down Expand Up @@ -67,8 +67,8 @@ services:
- 5432:5432
volumes:
- ./tmp:/tmp2

pdf:
image: hassananv/weasyprint
ports:
- 8083:5001
- 8083:5001
2 changes: 1 addition & 1 deletion web/src/components/DutyRoster/DutyRosterWeekView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
</template>

<template v-slot:cell(h0)="data" >
<duty-card-week-view v-on:change="getData" :dutyRosterInfo="data.item"/>
<duty-card-week-view v-on:change="getData" :dutyRostersJson="dutyRostersJson" :dutyRosterInfo="data.item"/>
</template>
</b-table>

Expand Down Expand Up @@ -120,7 +120,7 @@
public zoomLevel!: number;

@Prop({required: true})
runMethod!: any;

Check warning on line 123 in web/src/components/DutyRoster/DutyRosterWeekView.vue

View workflow job for this annotation

GitHub Actions / build (10.x)

Unexpected any. Specify a different type

isDutyRosterDataMounted = false;
hasPermissionToAddAssignments = false;
Expand Down Expand Up @@ -243,7 +243,7 @@

public extractTeamShiftInfo(shiftsJson){
this.UpdateShiftAvailabilityInfo([]);
const allDutySlots: any[] = []

Check warning on line 246 in web/src/components/DutyRoster/DutyRosterWeekView.vue

View workflow job for this annotation

GitHub Actions / build (10.x)

Unexpected any. Specify a different type
for(const dutyRoster of this.dutyRostersJson)
allDutySlots.push(...dutyRoster.dutySlots)

Expand Down
44 changes: 36 additions & 8 deletions web/src/components/DutyRoster/components/DutyCardWeekView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,10 @@
<b-button style="margin:-2.95rem -0.75rem -2rem 1rem; width:2.5rem; height:2.5rem;" variant="outline-warning" class="text-light" @click="closeEditDutySheriffModalWindow()"
><div style="transform:translate(0px,1px)">&times;</div></b-button>
</template>
</b-modal>

</b-modal>

<bulk-unassign-modal :showModal="showBulkUnassignModal" :dutyRostersJson="dutyRostersJson" @close="closeBulkUnassignModelWindow($event)"/>

</div>
</template>

Expand All @@ -277,9 +279,10 @@
import * as _ from 'underscore';
import moment from 'moment-timezone';
import AddDutySlotWeekForm from './AddDutySlotWeekForm.vue'
import {dutyRangeInfoType, dutySlotInfoType, assignDutySlotsInfoType, assignDutyInfoType, assignmentCardInfoType, dutyBlockWeekInfoType, myTeamShiftInfoType, selectedDutyCardInfoType, assignmentCardWeekInfoType } from '@/types/DutyRoster';
import {dutyRangeInfoType, dutySlotInfoType, assignDutySlotsInfoType, assignDutyInfoType, assignmentCardInfoType, dutyBlockWeekInfoType, myTeamShiftInfoType, selectedDutyCardInfoType, assignmentCardWeekInfoType, attachedDutyInfoType } from '@/types/DutyRoster';
import {localTimeInfoType, userInfoType} from '@/types/common';
import SheriffModal from './SheriffModal.vue'
import SheriffModal from './SheriffModal.vue';
import BulkUnassignModal from '../../common/BulkUnassignModal.vue';

import { namespace } from "vuex-class";
import "@store/modules/CommonInformation";
Expand All @@ -290,7 +293,8 @@
@Component({
components: {
AddDutySlotWeekForm,
SheriffModal
SheriffModal,
BulkUnassignModal
}
})
export default class DutyCardWeekView extends Vue {
Expand All @@ -304,6 +308,9 @@
@commonState.State
public userDetails!: userInfoType;

@Prop({required: true})
dutyRostersJson!: attachedDutyInfoType[];

@Prop({required: true})
dutyRosterInfo!: assignmentCardInfoType;

Expand Down Expand Up @@ -370,13 +377,15 @@
showEditDutySheriffModal = false;
editingBlockId=''

showBulkUnassignModal = false;

editDutyError = false;
editDutyErrorMsg = '';

deleteErrorMsg = '';
deleteError = false;

selectedComments: any[] = [];

Check warning on line 388 in web/src/components/DutyRoster/components/DutyCardWeekView.vue

View workflow job for this annotation

GitHub Actions / build (10.x)

Unexpected any. Specify a different type
selectedComment = '';

commentSaved = false;
Expand Down Expand Up @@ -424,7 +433,7 @@
}


public editDutySheriffModal(day, e?, blkId?){
public editDutySheriffModal(day, e?, blkId?){
if(e?.ctrlKey == true){
const block = this.dutyBlocks.filter(blk => blk.id==blkId)
const assignment = this.dutyRosterInfo.assignment+'D'+day
Expand All @@ -441,7 +450,10 @@
}
this.UpdateSelectedDuties(selectedDuties)
}
else if (blkId.includes('i')){
else if (this.selectedDuties.length > 1 && this.shouldAllowBulkUnassign()) {
this.showBulkUnassignModal = true;

} else if (blkId.includes('i')){
this.editDuty(day)
}
else{
Expand All @@ -453,7 +465,17 @@
}
}

shouldAllowBulkUnassign() {
// show if all the selectedDuites are assigned to a sheriff
return this.selectedDuties.every((s) =>
s.dutyBlock?.every((d) => {
if (!d.sheriffId || !d.dutyId) return false;
return true;
})
)
}

public editDuty(day, e?, blkId?){

Check warning on line 478 in web/src/components/DutyRoster/components/DutyCardWeekView.vue

View workflow job for this annotation

GitHub Actions / build (10.x)

'e' is defined but never used
this.isDutyDataMounted = false;
this.dutyBlocksDay = (this.dutyBlocks.filter(dutyBlock=>{if(dutyBlock.day==day)return true;}));
this.selectedComment = this.dutyBlocksDay[0].comment
Expand Down Expand Up @@ -544,6 +566,13 @@
this.showEditDutySheriffModal = false;
}

public closeBulkUnassignModelWindow(refreshData: boolean) {
this.showBulkUnassignModal = false;
if (refreshData) {
this.$emit('change', this.scrollPositions());
}
}

public closeDutySlotForm() {
this.addNewDutySlotForm= false;
this.addFormColor = 'secondary';
Expand Down Expand Up @@ -571,7 +600,6 @@
}

public extractDuty(){

for(let day=0; day<7; day++){
if(this.dutyRosterInfo[day]){
const dutyInfo = this.dutyRosterInfo[day];
Expand Down
192 changes: 192 additions & 0 deletions web/src/components/common/BulkUnassignModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
<template>
<div>
<b-modal v-model="showModal" id="bv-modal-bulk-unassign" size="lg bulk-unassign" no-close-on-backdrop centered header-class="bg-primary pt-3 pb-2 text-light">
<template v-slot:modal-header-close>
<b-button style="margin:-2.95rem -0.75rem -2rem 1rem; width:2.5rem; height:2.5rem;" variant="outline-warning" class="text-light" @click="closeBulkUnassignModal">
<div style="transform:translate(0px,1px)">&times;</div>
</b-button>
</template>

<template v-slot:modal-title>
<div class="h2 mb-2 text-light"> Bulk Unassign: </div>
</template>

<loading-spinner v-if="loadingData" />

<h1> Duty Slots </h1>

<b-row v-if="showBulkUnassignError" id="BulkUnassignError" class="h4 mx-2">
<b-badge class="mx-1 mt-2"
style="width: 20rem;"
v-b-tooltip.hover
:title="showBulkUnassignErrorMsg"
variant="danger">

{{showBulkUnassignErrorMsg | truncate(40)}}

<b-icon class="ml-3"
icon = x-square-fill
@click="showBulkUnassignError = false"
/>
</b-badge>
</b-row>

<b-table
:items="dutyBlocksToUnassign"
:fields="fields"
head-row-variant="primary"
striped
borderless
small
sort-by="startTimeString"
responsive="sm"
>
<template v-slot:cell(sheriffName)="data">
{{ data.item.firstName }} {{ data.item.lastName }}
</template>

<template v-slot:cell(dutyDate)="data">
{{ data.value|beautify-date-weekday }}
</template>
</b-table>

<template v-slot:modal-footer>
<b-button variant="danger" class="mr-auto" @click="confirmBulkUnassign()">
<b-icon-trash-fill style="padding:0; vertical-align: middle; margin-right: 0.25rem;"></b-icon-trash-fill>
Unassign
</b-button>
<b-button variant="primary" @click="closeBulkUnassignModal">Cancel</b-button>
</template>
</b-modal>

<!-- confirm bulk unassign modal -->
<b-modal v-model="showConfirmBulkUnassignModal" id="bv-modal-confirm-bulk-unassign" header-class="bg-warning text-light">
<template v-slot:modal-title>
<h2 class="mb-0 text-light">Confirm Bulk Unassign Duties</h2>
</template>

<h4 >Are you sure you want to bulk unassign the below list of assignments? Please double check the list of assignments.</h4>

<template v-slot:modal-footer>
<b-button variant="danger" @click="bulkUnassign()">Confirm</b-button>
<b-button variant="primary" @click="$bvModal.hide('bv-modal-confirm-bulk-unassign')">Cancel</b-button>
</template>
<template v-slot:modal-header-close>
<b-button variant="outline-warning" class="text-light closeButton" @click="$bvModal.hide('bv-modal-confirm-bulk-unassign')"
>&times;</b-button>
</template>
</b-modal>
</div>
</template>

<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
import { assignDutyInfoType, assignmentCardWeekInfoType, attachedDutyInfoType, dutyBlockWeekInfoType, selectedDutyCardInfoType} from '@/types/DutyRoster';

import * as _ from 'underscore';

import { namespace } from "vuex-class";

import "@store/modules/DutyRosterInformation";
const dutyState = namespace("DutyRosterInformation");

@Component({
components: {
}
})
export default class BulkUnassignModal extends Vue {
@Prop({required: true})
showModal = false;

@Prop({required: true})
dutyRostersJson!: attachedDutyInfoType[];

@dutyState.State
public dutyRosterAssignmentsWeek!: assignmentCardWeekInfoType[];

@dutyState.State
public selectedDuties!: selectedDutyCardInfoType[];

loadingData = false;

showConfirmBulkUnassignModal = false;

fields = [
{key:'assignmentName', label:'Assignment', sortable:false, tdClass: 'border-top', thClass:'h6 align-middle',},
{key:'sheriffName', label:'Sheriff', sortable:false, tdClass: 'border-top', thClass:'h6 align-middle',},
{key:'dutyDate', label:'Duty Date', sortable:false, tdClass: 'border-top', thClass:'h6 align-middle',},
{key:'startTimeString', label:'Start Time', sortable:false, tdClass: 'border-top', thClass:'h6 align-middle',},
{key:'endTimeString', label:'End Time', sortable:false, tdClass: 'border-top', thClass:'h6 align-middle',},
];

dutyBlocksToUnassign: Array<dutyBlockWeekInfoType & { assignmentName: string }> = [];

showBulkUnassignError = false;
showBulkUnassignErrorMsg = '';

mounted() {
this.$root.$on('bv::modal::show', (bvEvent, modalId) => {

const constructDutyBlocks: Array<dutyBlockWeekInfoType & { assignmentName: string }> = [];
this.selectedDuties.forEach((sd) => {
sd.dutyBlock?.forEach((db) => {
const assignmentId = this.dutyRostersJson.find((dr) => dr.id === db.dutyId)?.assignmentId;
const findAssignment = this.dutyRosterAssignmentsWeek.find((aw) => aw.assignmentDetail.id === assignmentId);
const assignmentName = findAssignment?.assignmentDetail?.name ? findAssignment.assignmentDetail.name : '';

constructDutyBlocks.push({...db, assignmentName: assignmentName});
});
});

this.dutyBlocksToUnassign = constructDutyBlocks;
});
}

confirmBulkUnassign() {
this.showConfirmBulkUnassignModal = true;
}

bulkUnassign() {
this.showConfirmBulkUnassignModal = false;

// remove the selected dutyslots from DutyRoster.dutySlots[]
const dutyIds = _.flatten(this.selectedDuties.map((s) => s.dutyBlock?.map((d) => d.dutyId)))
const dutySlotIds = _.flatten(this.selectedDuties.map((s) => s.dutyBlock?.map((d) => d.dutySlotId)));
const updateDutyRosterDto: assignDutyInfoType[] = this.dutyRostersJson.filter((s) => {
if (dutyIds.includes(s.id)) {
s.dutySlots = s.dutySlots.filter((ds) => !dutySlotIds.includes(ds.id));
return true;
}

return false;
});

this.showBulkUnassignError = false;
this.loadingData = true;

const updateDutyRosterUrl = 'api/dutyroster';
this.$http.put(updateDutyRosterUrl, updateDutyRosterDto).then(response => {
if(response.data){
this.$emit('close', true);
}

this.showBulkUnassignError = false;
this.loadingData = false;
}, err => {
this.showBulkUnassignErrorMsg = 'Error Bulk unassigning duty slots';
if (err.response.data.error) {
this.showBulkUnassignErrorMsg = err.response.data.error;
}

this.showBulkUnassignError = true;
this.loadingData = false;
});
}

closeBulkUnassignModal() {
this.showBulkUnassignError = false;
this.showModal = false;
this.$emit('close', false);
}
}
</script>
Loading