Skip to content

Commit

Permalink
Merge pull request #121 from hotwax/115_facility_logins
Browse files Browse the repository at this point in the history
Implemented: facility login functionality (#115).
  • Loading branch information
ravilodhi authored Dec 18, 2023
2 parents aad513b + e9a6d73 commit 5e4b439
Show file tree
Hide file tree
Showing 12 changed files with 755 additions and 45 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ VUE_APP_BASE_URL=
VUE_APP_PERMISSION_ID=
VUE_APP_LOCALES={"en-US": "English"}
VUE_APP_DEFAULT_LOG_LEVEL="error"
VUE_APP_LOGIN_URL="http://launchpad.hotwax.io/login"
VUE_APP_LOGIN_URL="http://launchpad.hotwax.io/login"
VUE_APP_USERS_APPLICATION_URL="http://users.hotwax.io"
171 changes: 171 additions & 0 deletions src/components/CreateFacilityLoginModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<template>
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="closeModal">
<ion-icon slot="icon-only" :icon="closeOutline" />
</ion-button>
</ion-buttons>
<ion-title>{{ `Create ${parentFacilityTypeDesc} login` }}</ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<ion-list>
<ion-item>
<ion-label class="ion-text-wrap" position="floating">
{{ translate('Username') }} <ion-text color="danger">*</ion-text>
</ion-label>
<ion-input v-model="username" />
</ion-item>
<ion-item ref="password">
<ion-label class="ion-text-wrap" position="floating">
{{ translate('Password') }} <ion-text color="danger">*</ion-text>
</ion-label>
<ion-input v-model="password" @keyup="validatePassword" @ionBlur="markPasswordTouched" type="password" />
<ion-note slot="helper">
{{ translate('Password should be at least 5 characters long, it contains at least one number, one alphabet and one special character.') }}
</ion-note>
</ion-item>
<ion-item>
<ion-label position="floating">{{ translate('Reset password email') }} <ion-text
color="danger">*</ion-text></ion-label>
<ion-input v-model="emailAddress"></ion-input>
</ion-item>
</ion-list>

<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button :disabled="checkCreateUserButtonStatus()" @click="createFacilityLogin()">
<ion-icon :icon="lockClosedOutline" />
</ion-fab-button>
</ion-fab>
</ion-content>
</template>

<script lang="ts">
import {
IonButtons,
IonButton,
IonContent,
IonFab,
IonFabButton,
IonHeader,
IonIcon,
IonInput,
IonItem,
IonLabel,
IonList,
IonNote,
IonTitle,
IonToolbar,
modalController
} from "@ionic/vue";
import { defineComponent } from "vue";
import {
closeOutline,
eyeOutline,
eyeOffOutline,
lockClosedOutline,
mailOutline
} from "ionicons/icons";
import { useStore } from "vuex";
import { translate } from '@hotwax/dxp-components'
import { isValidEmail, isValidPassword, showToast } from "@/utils";
import { FacilityService } from "@/services/FacilityService";
import { UserService } from "@/services/UserService";
import emitter from "@/event-bus";
export default defineComponent({
name: "CustomFieldModal",
components: {
IonButtons,
IonButton,
IonContent,
IonFab,
IonFabButton,
IonHeader,
IonIcon,
IonInput,
IonItem,
IonLabel,
IonList,
IonNote,
IonTitle,
IonToolbar,
},
data() {
return {
username: this.currentFacility?.facilityId,
password: '',
emailAddress: '',
}
},
props: ["currentFacility", "parentFacilityTypeDesc"],
methods: {
closeModal() {
modalController.dismiss({ dismissed: true });
},
async createFacilityLogin() {
if (!this.username) {
showToast(translate('Username is required.'))
return
} else if (await UserService.isUserLoginIdExists(this.username)) {
showToast(translate('Could not create login user: user with ID already exists.', { userLoginId: this.username }))
return;
}
try {
const payload = {
"facilityId": this.currentFacility?.facilityId,
"facilityName": this.currentFacility?.facilityName,
"username": this.username,
"password": this.password,
"emailAddress": this.emailAddress
}
emitter.emit('presentLoader')
await FacilityService.createFacilityLogin(payload);
showToast(translate('Facility login created.'))
await this.store.dispatch('facility/fetchFacilityLogins', { facilityId: this.currentFacility?.facilityId });
} catch (error) {
showToast(translate('Failed to create facility login.'))
}
this.closeModal();
emitter.emit('dismissLoader')
},
checkCreateUserButtonStatus() {
return ((!this.username.length || !this.password.length) || !isValidPassword(this.password) || !isValidEmail(this.emailAddress));
},
validatePassword(event: any) {
const value = event.target.value;
(this as any).$refs.password.$el.classList.remove('ion-valid');
(this as any).$refs.password.$el.classList.remove('ion-invalid');
if (value === '') return;
isValidPassword(value)
? (this as any).$refs.password.$el.classList.add('ion-valid')
: (this as any).$refs.password.$el.classList.add('ion-invalid');
},
markPasswordTouched() {
(this as any).$refs.password.$el.classList.add('ion-touched');
},
markConfirmPasswordTouched() {
(this as any).$refs.confirmPassword.$el.classList.add('ion-touched');
},
},
setup() {
const store = useStore();
return {
closeOutline,
eyeOutline,
eyeOffOutline,
lockClosedOutline,
mailOutline,
store,
translate
};
},
});
</script>

206 changes: 206 additions & 0 deletions src/components/FacilityLoginActionPopover.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<template>
<ion-content>
<ion-list>
<ion-list-header>{{ currentFacilityUser?.groupName }}</ion-list-header>
<ion-item button @click="viewDetails()">
{{ translate("View details") }}
<ion-icon slot="end" :icon="keyOutline" />
</ion-item>
<ion-item button @click="sendResetPasswordEmail()">
{{ translate("Reset password email") }}
<ion-icon slot="end" :icon="mailOutline" />
</ion-item>
<ion-item button lines="none" @click="unlinkFacilityLoginAlert()">
{{ translate("Unlink") }}
<ion-icon slot="end" :icon="removeCircleOutline" />
</ion-item>
</ion-list>
</ion-content>
</template>

<script lang="ts">
import {
IonContent,
IonIcon,
IonItem,
IonList,
IonListHeader,
alertController,
popoverController
} from "@ionic/vue";
import { defineComponent } from "vue";
import { removeCircleOutline, mailOutline, keyOutline } from "ionicons/icons";
import { translate } from "@hotwax/dxp-components";
import { useStore } from "vuex";
import { FacilityService } from "@/services/FacilityService";
import { UserService } from "@/services/UserService"
import { DateTime } from "luxon";
import { hasError } from "@/adapter";
import { showToast } from "@/utils";
import logger from "@/logger";
import emitter from "@/event-bus";
export default defineComponent({
name: "ProductStorePopover",
components: {
IonContent,
IonIcon,
IonItem,
IonList,
IonListHeader
},
props: ['currentFacility', 'currentFacilityUser', "parentFacilityTypeDesc"],
methods: {
async viewDetails() {
const userDetailUrl = `${process.env.VUE_APP_USERS_APPLICATION_URL}/user-details/${this.currentFacilityUser.partyId}`
window.location.href = userDetailUrl
popoverController.dismiss()
},
async sendResetPasswordEmail() {
try {
const resp = await UserService.sendResetPasswordEmail({
emailAddress: this.currentFacilityUser.infoString,
userName: this.currentFacilityUser.userLoginId
})
if (!hasError(resp)) {
showToast(translate('Password reset email sent successfully.'))
} else {
throw resp.data
}
} catch (error) {
showToast(translate('Failed to send password reset email.'))
console.error(error)
}
popoverController.dismiss()
},
async removePartyFromFacilityCompletely(payload: any) {
try {
//fetching all the roles in which the user is associated with the facility
let resp = await FacilityService.getFacilityParties({
inputFields: {
"partyId": payload.partyId,
"facilityId": payload.facilityId,
},
fieldList: ['facilityId', 'partyId', 'roleTypeId', 'fromDate'],
entityName: "FacilityParty",
distinct: 'Y',
noConditionFind: 'Y',
filterByDate: 'Y',
viewSize: 50
})
if (!hasError(resp) && resp.data.count > 0) {
const facilityParties = resp.data.docs;
const promises = [] as any;
facilityParties.forEach((facilityParty: any) => {
promises.push(FacilityService.removePartyFromFacility({
...facilityParty,
thruDate: DateTime.now().toMillis(),
}))
});
await Promise.all(promises).then(responses => {
responses.forEach(response => {
if (hasError(response)) {
throw response.data;
}
});
})
} else {
throw resp.data;
}
} catch (err) {
showToast(translate('Failed to remove party from facility'))
logger.error('Failed to remove party from facility', err)
return;
}
},
async unlinkFacilityLogin(data: any) {
emitter.emit('presentLoader')
try {
//Unlinking user from facility will only remove FAC_LOGIN role from facility
if (data === 'UNLINK') {
let resp = await FacilityService.removePartyFromFacility({
facilityId: this.currentFacilityUser.facilityId,
partyId: this.currentFacilityUser.partyId,
roleTypeId: this.currentFacilityUser.roleTypeId,
fromDate: this.currentFacilityUser.fromDate,
thruDate: DateTime.now().toMillis(),
})
if (!hasError(resp)) {
showToast(translate("Facility login removed."))
} else {
throw resp.data;
}
}
//Blocking user will remove all the roles from facility in which the user is associated, also block the userlogin.
if (data === 'BLOCK') {
await this.removePartyFromFacilityCompletely({ facilityId: this.currentFacility.facilityId, partyId: this.currentFacilityUser.partyId }) as any;
const resp = await UserService.updateUserLoginStatus({
enabled: 'N',
partyId: this.currentFacilityUser.partyId,
userLoginId: this.currentFacilityUser.userLoginId
});
if (!hasError(resp)) {
showToast(translate("Facility login removed."))
} else {
throw resp.data;
}
}
//fetching updated facility logins
await this.store.dispatch('facility/fetchFacilityLogins', { facilityId: this.currentFacility?.facilityId })
} catch (err) {
showToast(translate("Failed to remove facility login."))
logger.error(err)
}
emitter.emit('dismissLoader')
popoverController.dismiss()
},
async unlinkFacilityLoginAlert() {
const message = 'Unlinking this login as an official facility login will not prevent this user from being used to login at this facility. Do you also want to block this user from logging into this facility?'
const alert = await alertController.create({
header: translate(`Unlink ${this.parentFacilityTypeDesc} login`),
message: translate(message, { space: "<br><br>" }),
inputs: [
{
label: translate('Unlink'),
type: 'radio',
value: 'UNLINK',
checked: true
},
{
label: translate('Unlink and block'),
type: 'radio',
value: 'BLOCK',
},
],
buttons: [
{
text: translate("Cancel"),
},
{
text: translate("Confirm"),
handler: async (data: any) => {
await this.unlinkFacilityLogin(data);
}
}
],
});
return alert.present();
}
},
setup() {
const store = useStore();
return {
removeCircleOutline,
keyOutline,
mailOutline,
store,
translate
};
}
});
</script>
Loading

0 comments on commit 5e4b439

Please sign in to comment.