diff --git a/.env.example b/.env.example index a0a75169..f0da745a 100644 --- a/.env.example +++ b/.env.example @@ -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" \ No newline at end of file +VUE_APP_LOGIN_URL="http://launchpad.hotwax.io/login" +VUE_APP_USERS_APPLICATION_URL="http://users.hotwax.io" \ No newline at end of file diff --git a/src/components/CreateFacilityLoginModal.vue b/src/components/CreateFacilityLoginModal.vue new file mode 100644 index 00000000..951c7932 --- /dev/null +++ b/src/components/CreateFacilityLoginModal.vue @@ -0,0 +1,171 @@ + + + + \ No newline at end of file diff --git a/src/components/FacilityLoginActionPopover.vue b/src/components/FacilityLoginActionPopover.vue new file mode 100644 index 00000000..d0c0ce14 --- /dev/null +++ b/src/components/FacilityLoginActionPopover.vue @@ -0,0 +1,206 @@ + + + \ No newline at end of file diff --git a/src/locales/en.json b/src/locales/en.json index bedce00c..e11db7fc 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -39,11 +39,14 @@ "Closing Time": "Closing Time", "Configure settings later": "Configure settings later", "Configure the order fulfillment capacity of your facility.": "Configure the order fulfillment capacity of your facility.", + "Confirm": "Confirm", "Consumed Order Limit": "Consumed Order Limit", + "Create Distribution Center login": "Create Distribution Center login", "Create group": "Create group", "Create login credentials": "Create login credentials", "Create Outlet Store": "Create Outlet Store", "Create Outlet Warehouse": "Create Outlet Warehouse", + "Create Physical Store login": "Create Physical Store login", "Create Retail Store": "Create Retail Store", "Create Warehouse": "Create Warehouse", "Custom": "Custom", @@ -59,6 +62,7 @@ "Description": "Description", "Dismiss": "Dismiss", "Distribution Center": "Distribution Center", + "Distribution Center login": "Distribution Center login", "Failed to associate calendar to the facility.": "Failed to associate calendar to the facility.", "Filters": "Filters", "Edit": "Edit", @@ -85,6 +89,8 @@ "Facility location created successfully": "Facility location created successfully", "Facility location removed successfully": "Facility location removed successfully", "Facility location updated successfully": "Facility location updated successfully", + "Facility login created.": "Facility login created.", + "Facility login removed.": "Facility login removed.", "Facility name": "Facility name", "Facility name is required.": "Facility name is required.", "Facility Management": "Facility Management", @@ -97,6 +103,7 @@ "Failed to create calendar to the facility.": "Failed to create calendar to the facility.", "Failed to create external mapping": "Failed to create external mapping", "Failed to create facility.": "Failed to create facility.", + "Failed to create facility login." : "Failed to create facility login.", "Failed to create parking.": "Failed to create parking.", "Failed to create facility address.": "Failed to create facility address.", "Failed to create facility group.": "Failed to create facility group.", @@ -114,6 +121,7 @@ "Failed to rename facility.": "Failed to rename facility.", "Failed to remove facility latitude and longitude.": "Failed to remove facility latitude and longitude.", "Failed to remove facility location": "Failed to remove facility location", + "Failed to remove facility login.": "Failed to remove facility login.", "Failed to remove facility mapping": "Failed to remove facility mapping", "Failed to remove party from facility.": "Failed to remove party from facility.", "Failed to remove shopify mapping": "Failed to remove shopify mapping", @@ -134,6 +142,7 @@ "Failed to update some fulfillment settings.": "Failed to update some fulfillment settings.", "Failed to update some product stores": "Failed to update some product stores", "Failed to update some role(s).": "Failed to update some role(s).", + "Failed to send password reset email.": "Failed to send password reset email.", "Fetching TimeZones": "Fetching TimeZones", "Find Facilities": "Find Facilities", "Friday": "Friday", @@ -207,12 +216,15 @@ "Parking unarchived successfully.": "Parking unarchived successfully.", "Party Id": "Party Id", "Party successfully removed from facility.": "Party successfully removed from facility.", + "Password": "Password", + "Password reset email sent successfully.": "Password reset email sent successfully.", + "Password should be at least 5 characters long, it contains at least one number, one alphabet and one special character.": "Password should be at least 5 characters long, it contains at least one number, one alphabet and one special character.", + "party id": "party id", "Pending allocation": "Pending allocation", "Physical Store": "Physical Store", + "Physical Store login": "Physical Store login", "Please check start time and end time entries. End time cannot be less than start time.": "Please check start time and end time entries. End time cannot be less than start time.", "Please update atleast one party role.": "Please update atleast one party role.", - "Password": "Password", - "party id": "party id", "Please contact the administrator.": "Please contact the administrator.", "Please enter a valid value": "Please enter a valid value", "Please fill all the required fields": "Please fill all the required fields", @@ -235,6 +247,7 @@ "Rename facility": "Rename facility", "Rename parking": "Rename parking", "Reset": "Reset", + "Reset password email": "Reset password email", "Role": "Role", "role": "role", "Saturday": "Saturday", @@ -298,12 +311,15 @@ "Unlimited fulfillment capacity": "Unlimited fulfillment capacity", "Unlimited orders": "Unlimited orders", "Unlink": "Unlink", + "Unlink and block": "Unlink and block", + "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?": "Unlinking this login as an official facility login will not prevent this user from being used to login at this facility. {space} Do you also want to block this user from logging into this facility?", "Update days to ship": "Update days to ship", "Updated default days to ship": "Updated default days to ship", "Username": "Username", "Username is required.": "Username is required.", "Uses native fulfillment app": "Uses native fulfillment app", "Version: ": "Version: { appVersion }", + "View details": "View details", "View order count history": "View order count history", "View other schedules": "View other schedules", "Wednesday": "Wednesday", diff --git a/src/services/FacilityService.ts b/src/services/FacilityService.ts index d7b75252..d9bfdf7a 100644 --- a/src/services/FacilityService.ts +++ b/src/services/FacilityService.ts @@ -2,6 +2,7 @@ import { api, hasError } from '@/adapter'; import logger from '@/logger'; import { DateTime } from 'luxon'; import { prepareOrderQuery } from '@/utils/solrHelper'; +import { UserService } from './UserService'; const createFacilityPostalAddress = async (payload: any): Promise => { return api({ @@ -576,6 +577,84 @@ const deleteFacilityGroup = async (payload: any): Promise => { }) } +const createFacilityLogin = async (payload: any): Promise => { + try { + //Create role type if not exists. This is required for associating facility login user to facility. + if (!await UserService.isRoleTypeExists("FAC_LOGIN")) { + const resp = await UserService.createRoleType({ + "roleTypeId": "FAC_LOGIN", + "description": "Facility Login", + }) + if (hasError(resp)) { + throw resp.data; + } + } + + const params = { + "groupName": payload.facilityName, + "partyTypeId": "PARTY_GROUP", + "partyIdFrom": "COMPANY", + "roleTypeIdFrom": "INTERNAL_ORGANIZATIO", // not a typo + "roleTypeIdTo": "APPLICATION_USER", + "partyRelationshipTypeId": "EMPLOYMENT" + } + + let resp = await UserService.createRelationship(params); + if (hasError(resp)) { + throw resp.data; + } + const partyId = resp.data.partyId; + + resp = await UserService.createNewUserLogin({ + "partyId": partyId, + "userLoginId": payload.username, + "currentPassword": payload.password, + "currentPasswordVerify": payload.password, + "requirePasswordChange": "N", + "enabled": "Y", + "userPrefTypeId": "ORGANIZATION_PARTY", + "userPrefValue": "COMPANY" + }); + if (hasError(resp)) { + throw resp.data; + } + + const promises = []; + promises.push(UserService.addUserToSecurityGroup({ + "partyIdTo": partyId, + "securityGroupId": "STORE_MANAGER", + })); + if (payload.emailAddress) { + promises.push(UserService.createUpdatePartyEmailAddress({ + "partyId": partyId, + "emailAddress": payload.emailAddress, + "contactMechPurposeTypeId": "PRIMARY_EMAIL", + })); + } + promises.push(addPartyToFacility({ + "partyId": partyId, + "facilityId": payload.facilityId, + "roleTypeId": "WAREHOUSE_MANAGER" + })); + promises.push(addPartyToFacility({ + "partyId": partyId, + "facilityId": payload.facilityId, + "roleTypeId": "FAC_LOGIN" + })); + await Promise.all(promises).then(responses => { + responses.forEach(response => { + if (hasError(response)) { + throw response.data; + } + }); + }) + } catch (error: any) { + return Promise.reject(error) + } +} + + + export const FacilityService = { addFacilityToGroup, addPartyToFacility, @@ -592,6 +671,7 @@ export const FacilityService = { createShopifyShopLocation, deleteFacilityGroup, deleteFacilityLocation, + createFacilityLogin, deleteShopifyShopLocation, fetchArchivedFacilities, fetchFacilityGroup, diff --git a/src/services/UserService.ts b/src/services/UserService.ts index c4415b5c..e864f212 100644 --- a/src/services/UserService.ts +++ b/src/services/UserService.ts @@ -136,7 +136,7 @@ const getUserProfile = async (token: any): Promise => { } } -const createFacilityUser = async (payload: any): Promise => { +const createRelationship = async (payload: any): Promise => { return api({ url: "service/createRelationship", method: "post", @@ -144,20 +144,136 @@ const createFacilityUser = async (payload: any): Promise => { }); } -const addPartyToFacility = async (payload: any): Promise => { +const createNewUserLogin = async (payload: any): Promise => { return api({ - url: "service/addPartyToFacility", + url: "service/createNewUserLoginAndSetUserPreference", method: "post", data: payload }); } +const addUserToSecurityGroup = async (payload: any): Promise => { + return api({ + url: "service/addSecurityGroupToUserLogin", + method: "post", + data: payload + }); +} + +const isUserLoginIdExists = async(username: string): Promise => { + try { + const resp = await api({ + url: 'performFind', + method: 'POST', + data: { + entityName: "UserLogin", + inputFields: { + userLoginId: username + }, + viewSize: 1, + fieldList: ['userLoginId', 'partyId'], + distinct: 'Y', + noConditionFind: 'Y' + } + }) as any; + + if (!hasError(resp) && resp.data.docs.length) { + return true + } + return false + } catch(err) { + return false + } +} + +const createRoleType = async (payload: any): Promise => { + return api({ + url: "service/createRoleType", + method: "post", + data: payload + }); +} + +const isRoleTypeExists = async(roleTypeId: string): Promise => { + try { + + const resp = await api({ + url: 'performFind', + method: 'POST', + data: { + entityName: "RoleType", + inputFields: { + roleTypeId: roleTypeId + }, + viewSize: 1, + fieldList: ['roleTypeId'], + noConditionFind: 'Y' + } + }) as any + if (!hasError(resp) && resp.data.docs.length) { + return true + } + return false + } catch(err) { + return false + } +} + +const sendResetPasswordEmail = async (payload: any): Promise => { + return api({ + url: "sendResetPasswordMail", + method: "post", + data: payload + }); +} + +const updateUserLoginStatus = async (payload: any): Promise => { + return api({ + url: "service/updateUserLoginStatus", + method: "post", + data: payload + }); +} + +const createUpdatePartyEmailAddress = async (payload: any): Promise => { + return api({ + url: "service/createUpdatePartyEmailAddress", + method: "post", + data: payload + }); +} + +const fetchUserLoginAndPartyDetails = async (payload: any): Promise => { + return api({ + url: 'performFind', + method: 'POST', + data: payload + }) +} + +const fetchUserContactDetails = async (payload: any): Promise => { + return api({ + url: 'performFind', + method: 'POST', + data: payload + }) +} + export const UserService = { - addPartyToFacility, + addUserToSecurityGroup, + createRelationship, + createNewUserLogin, + createRoleType, + createUpdatePartyEmailAddress, + isRoleTypeExists, + isUserLoginIdExists, login, - createFacilityUser, getAvailableTimeZones, getUserProfile, + getUserPermissions, + fetchUserContactDetails, + fetchUserLoginAndPartyDetails, + sendResetPasswordEmail, setUserTimeZone, - getUserPermissions + updateUserLoginStatus } \ No newline at end of file diff --git a/src/store/modules/facility/actions.ts b/src/store/modules/facility/actions.ts index ea8d342b..8d7d2277 100644 --- a/src/store/modules/facility/actions.ts +++ b/src/store/modules/facility/actions.ts @@ -3,6 +3,7 @@ import RootState from '@/store/RootState' import FacilityState from './FacilityState' import emitter from '@/event-bus' import { FacilityService } from '@/services/FacilityService' +import { UserService } from '@/services/UserService' import { hasError } from '@/adapter' import * as types from './mutation-types' import logger from '@/logger' @@ -387,7 +388,9 @@ const actions: ActionTree = { const params = { inputFields: { facilityId: payload.facilityId, - partyId_op: 'not-empty' + partyId_op: 'not-empty', + roleTypeId: 'FAC_LOGIN', + roleTypeId_op: 'notEqual' }, entityName: "FacilityAndParty", filterByDate: 'Y', @@ -440,6 +443,73 @@ const actions: ActionTree = { commit(types.FACILITY_SHOPIFY_MAPPINGS_UPDATED, shopifyFacilityMappings) }, + async fetchFacilityLogins({ commit }, payload) { + + let facilityLogins = [] as any; + let dataList = [] as any; + + try { + let resp = await FacilityService.getFacilityParties({ + inputFields: { + "facilityId": payload.facilityId, + "roleTypeId": "FAC_LOGIN", + }, + 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 + dataList = facilityParties; + const partyIds = facilityParties.map((party: any) => party.partyId); + + resp = await UserService.fetchUserLoginAndPartyDetails({ + inputFields: { + "partyId": partyIds, + "partyId_op": "in", + }, + fieldList: ['partyId', 'groupName', "userLoginId"], + entityName: "UserLoginAndPartyDetails", + distinct: 'Y', + noConditionFind: 'Y', + viewSize: 50 + }) + if (!hasError(resp) && resp.data.count > 0) { + dataList = [...dataList, ...resp.data.docs] + resp = await UserService.fetchUserContactDetails({ + inputFields: { + "partyId": partyIds, + "partyId_op": "in", + contactMechPurposeTypeId: 'PRIMARY_EMAIL', + }, + viewSize: 100, + filterByDate: 'Y', + entityName: 'PartyContactDetailByPurpose', + fieldList: ['partyId', 'infoString', 'contactMechId', 'contactMechPurposeTypeId'] + }) + if (!hasError(resp) && resp.data.count > 0) { + dataList = [...dataList, ...resp.data.docs] + } + const facilityPartyData = dataList.reduce((partyData:any, doc:any) => { + const partyId = doc.partyId; + partyData[partyId] = { ...partyData[partyId], ...doc }; + return partyData; + }, {}); + + facilityLogins = Object.values(facilityPartyData); + } + } else { + throw resp.data + } + } catch(err) { + logger.error('Failed to fetch facility parties', err) + } + commit(types.FACILITY_LOGINS_UPDATED, facilityLogins) + }, + async fetchVirtualFacilities({ commit, dispatch, state }, payload) { let facilities = [], total = 0; if (payload.viewIndex === 0) emitter.emit("presentLoader"); diff --git a/src/store/modules/facility/mutation-types.ts b/src/store/modules/facility/mutation-types.ts index 3ce2e054..0f5882f4 100644 --- a/src/store/modules/facility/mutation-types.ts +++ b/src/store/modules/facility/mutation-types.ts @@ -13,3 +13,4 @@ export const FACILITY_PRODUCT_STORES_UPDATED = SN_FACILITY + '/PRODUCT_STORES_UP export const FACILITY_VIRTUAL_FACILITY_LIST_UPDATED = SN_FACILITY + '/VIRTUAL_FACILITY_LIST_UPDATED' export const FACILITY_GROUP_LIST_UPDATED = SN_FACILITY + '/GROUP_LIST_UPDATED' export const FACILITY_ARCHIVED_UPDATED = SN_FACILITY + '/ARCHIVED_UPDATED' +export const FACILITY_LOGINS_UPDATED = SN_FACILITY + '/LOGINS_UPDATED' diff --git a/src/store/modules/facility/mutations.ts b/src/store/modules/facility/mutations.ts index 21c0b9f5..4d3ef518 100644 --- a/src/store/modules/facility/mutations.ts +++ b/src/store/modules/facility/mutations.ts @@ -40,6 +40,9 @@ const mutations: MutationTree = { [types.FACILITY_PARTIES_UPDATED](state, payload) { state.current.parties = payload }, + [types.FACILITY_LOGINS_UPDATED](state, payload) { + state.current.facilityLogins = payload + }, [types.FACILITY_VIRTUAL_FACILITY_LIST_UPDATED](state, payload) { state.virtualFacilities.list = payload.facilities state.virtualFacilities.total = payload.total diff --git a/src/utils/index.ts b/src/utils/index.ts index ae24a1a8..47be4740 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -46,6 +46,12 @@ const isValidPassword = (password : string) => { return passwordPattern.test(password); } +const isValidEmail = (email : string) => { + // Regular expression pattern for a valid email address + const emailPattern = /^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/; + return emailPattern.test(email); +} + // will sort the list but keeps the customValues at the beginning const customSort = (list: any, customValues: Array, sortParameter: string) => { return list.sort((first: any, second: any) => { @@ -59,4 +65,4 @@ const generateInternalId = (name: string) => { return name.trim().toUpperCase().split(' ').join('_'); } -export { copyToClipboard, customSort, generateInternalId, isValidPassword, showToast } +export { copyToClipboard, customSort, generateInternalId, isValidEmail, isValidPassword, showToast } diff --git a/src/views/AddFacilityConfig.vue b/src/views/AddFacilityConfig.vue index 2566ae14..6ad430a0 100644 --- a/src/views/AddFacilityConfig.vue +++ b/src/views/AddFacilityConfig.vue @@ -156,7 +156,7 @@ import { isValidPassword, showToast } from "@/utils"; import { hasError } from "@/adapter"; import logger from "@/logger"; import { FacilityService } from "@/services/FacilityService"; -import { UserService } from "@/services/UserService"; +import { UserService } from "@/services/UserService" import SelectProductStoreModal from '@/components/SelectProductStoreModal.vue'; import { DateTime } from "luxon"; @@ -209,7 +209,7 @@ export default defineComponent({ async ionViewWillEnter() { await this.store.dispatch('facility/fetchCurrentFacility', { facilityId: this.facilityId }) await this.store.dispatch('util/fetchProductStores') - this.username = this.current.facilityName + this.username = this.current.facilityId }, methods: { async saveFulfillmentSettings() { @@ -243,44 +243,36 @@ export default defineComponent({ throw { message: translate('Failed to update some fulfillment settings.') } } }, - async createFacilityUser() { - if (!this.username) { - showToast(translate('Username is required.')) - return - } + async createFacilityLogin() { + + try { const payload = { - "groupName": this.username, - "facilityId": this.facilityId, - "loginPassword": this.password, - "partyTypeId": "PARTY_GROUP", - "partyIdFrom": "COMPANY", - "roleTypeIdFrom": "INTERNAL_ORGANIZATIO", // not a typo - "roleTypeIdTo": "APPLICATION_USER", - "partyRelationshipTypeId": "EMPLOYMENT" - } - - const resp = await UserService.createFacilityUser(payload); - if (!hasError(resp)) { - const partyId = resp.data.partyId; - await UserService.addPartyToFacility({ - "partyId": partyId, - "facilityId": this.facilityId, - "roleTypeId": "WAREHOUSE_MANAGER" - }) - } else { - throw { message: translate('Failed to create facility login credentials.') } + "facilityId" : this.facilityId, + "facilityName": this.current.facilityName, + "username": this.username, + "password": this.password, } - return Promise.resolve(resp.data) + await FacilityService.createFacilityLogin(payload); + return Promise.resolve() } catch (error) { return Promise.reject(error); } }, async saveStoreConfig() { - if (this.createLoginCreds && !this.password) { - showToast(translate('Please provide a password.')) - return + if (this.createLoginCreds) { + 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; + } + if (!this.password) { + showToast(translate('Please provide a password.')) + return + } } try { @@ -289,7 +281,7 @@ export default defineComponent({ } if (this.createLoginCreds) { - await this.createFacilityUser() + await this.createFacilityLogin() } if (this.selectedProductStores) { diff --git a/src/views/FacilityDetails.vue b/src/views/FacilityDetails.vue index 7c3725f5..6f107027 100644 --- a/src/views/FacilityDetails.vue +++ b/src/views/FacilityDetails.vue @@ -237,6 +237,34 @@ {{ translate("View order count history") }} + + + + {{ `${facilityTypes[current.parentFacilityTypeId]?.description} logins` }} + + + + {{ translate("Add") }} + + + + + + + + {{ facilityLogin.groupName }} +

{{ facilityLogin.partyId }}

+

{{ facilityLogin.userLoginId }}

+
+ + + +
+ + {{ translate("Add") }} + + +
@@ -483,6 +511,9 @@ import { showToast } from '@/utils'; import OperatingHoursPopover from '@/components/OperatingHoursPopover.vue' import GeoPointPopover from '@/components/GeoPointPopover.vue' import { UtilService } from '@/services/UtilService'; +import FacilityLoginActionPopover from '@/components/FacilityLoginActionPopover.vue' +import CreateFacilityLoginModal from '@/components/CreateFacilityLoginModal.vue' +import Image from '@/components/Image.vue'; import emitter from '@/event-bus' export default defineComponent({ @@ -515,6 +546,7 @@ export default defineComponent({ IonTitle, IonToggle, IonToolbar, + Image }, data() { return { @@ -557,7 +589,7 @@ export default defineComponent({ facilityTypeId: 'VIRTUAL_FACILITY', facilityTypeId_op: 'notEqual' })]) - await Promise.all([this.store.dispatch('facility/fetchFacilityLocations', { facilityId: this.facilityId }), this.store.dispatch('facility/getFacilityParties', { facilityId: this.facilityId }), this.store.dispatch('facility/fetchFacilityMappings', { facilityId: this.facilityId, facilityIdenTypeIds: Object.keys(this.externalMappingTypes)}), this.store.dispatch('facility/fetchShopifyFacilityMappings', { facilityId: this.facilityId }), this.store.dispatch('facility/getFacilityProductStores', { facilityId: this.facilityId }), this.store.dispatch('util/fetchProductStores'), this.store.dispatch('facility/fetchFacilityContactDetails', { facilityId: this.facilityId }), this.store.dispatch('util/fetchCalendars'), this.store.dispatch('facility/fetchFacilityCalendar', { facilityId: this.facilityId })]) + await Promise.all([this.store.dispatch('facility/fetchFacilityLocations', { facilityId: this.facilityId }), this.store.dispatch('facility/getFacilityParties', { facilityId: this.facilityId }), this.store.dispatch('facility/fetchFacilityMappings', { facilityId: this.facilityId, facilityIdenTypeIds: Object.keys(this.externalMappingTypes)}), this.store.dispatch('facility/fetchShopifyFacilityMappings', { facilityId: this.facilityId }), this.store.dispatch('facility/getFacilityProductStores', { facilityId: this.facilityId }), this.store.dispatch('util/fetchProductStores'), this.store.dispatch('facility/fetchFacilityContactDetails', { facilityId: this.facilityId }), this.store.dispatch('util/fetchCalendars'), this.store.dispatch('facility/fetchFacilityCalendar', { facilityId: this.facilityId }), this.store.dispatch('facility/fetchFacilityLogins', { facilityId: this.facilityId })]) this.defaultDaysToShip = this.current.defaultDaysToShip this.isLoading = false this.parentFacilityTypeId = this.current.parentFacilityTypeId @@ -1143,7 +1175,23 @@ export default defineComponent({ showToast(translate('Failed to update facility type.')) logger.error('Failed to update facility type.', error) } - } + }, + async openFacilityLoginActionPopover(ev: Event, facilityUser: any) { + const popover = await popoverController.create({ + component: FacilityLoginActionPopover, + componentProps: { currentFacility: this.current, currentFacilityUser: facilityUser, parentFacilityTypeDesc: this.facilityTypes[this.current.parentFacilityTypeId]?.description }, + event: ev, + showBackdrop: false + }); + return popover.present() + }, + async createFacilityLoginModal() { + const facilityLoginModal = await modalController.create({ + component: CreateFacilityLoginModal, + componentProps: { currentFacility: this.current, parentFacilityTypeDesc: this.facilityTypes[this.current.parentFacilityTypeId]?.description } + }) + facilityLoginModal.present() + }, }, setup() { const store = useStore();