diff --git a/strr-platform-web/app/assets/styles/layout.css b/strr-platform-web/app/assets/styles/layout.css
index 55c6e16c3..bc951bd79 100644
--- a/strr-platform-web/app/assets/styles/layout.css
+++ b/strr-platform-web/app/assets/styles/layout.css
@@ -3,7 +3,7 @@
}
.app-inner-container {
- @apply max-w-bcGovLg w-[100vw] mx-auto px-4;
+ @apply max-w-bcGovLg w-full mx-auto px-4;
}
.app-body {
diff --git a/strr-platform-web/app/components/connect/ButtonControl.vue b/strr-platform-web/app/components/connect/ButtonControl.vue
index b875ca9f7..b68be421f 100644
--- a/strr-platform-web/app/components/connect/ButtonControl.vue
+++ b/strr-platform-web/app/components/connect/ButtonControl.vue
@@ -21,6 +21,8 @@ const rightButtons = computed(() => buttonControl.value?.rightButtons || [])
:label="button.label"
:trailing="button.trailing || false"
:variant="button.variant || 'solid'"
+ :disabled="button.disabled || false"
+ :loading="button.loading || false"
data-testid="button-control-left-button"
@click="button.action()"
/>
diff --git a/strr-platform-web/app/components/connect/fee/Widget.vue b/strr-platform-web/app/components/connect/fee/Widget.vue
index 24e297488..e0afcabd0 100644
--- a/strr-platform-web/app/components/connect/fee/Widget.vue
+++ b/strr-platform-web/app/components/connect/fee/Widget.vue
@@ -36,7 +36,7 @@ watch(isFoldable, (val) => {
})
const toggleFolded = () => {
- if (isFoldable) {
+ if (isFoldable.value) {
folded.value = !folded.value
}
}
diff --git a/strr-platform-web/app/components/modal/Base.vue b/strr-platform-web/app/components/modal/Base.vue
new file mode 100644
index 000000000..ffbae4436
--- /dev/null
+++ b/strr-platform-web/app/components/modal/Base.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+ {{ content }}
+
+
+
+
+ {{ error.title }}
+
+
{{ error.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/strr-platform-web/app/composables/useStrrModals.ts b/strr-platform-web/app/composables/useStrrModals.ts
new file mode 100644
index 000000000..fc055fbd7
--- /dev/null
+++ b/strr-platform-web/app/composables/useStrrModals.ts
@@ -0,0 +1,26 @@
+// https://ui.nuxt.com/components/modal#control-programmatically
+import { ModalBase } from '#components'
+
+export const useStrrModals = () => {
+ const modal = useModal()
+ const { t } = useI18n()
+
+ function openAppSubmitError () {
+ modal.open(ModalBase, {
+ error: {
+ title: 'Error submitting application', // need to come up with different error messages for different scenarios
+ description: 'Some description here.'
+ },
+ actions: [{ label: t('btn.close'), handler: () => close() }]
+ })
+ }
+
+ function close () {
+ modal.close()
+ }
+
+ return {
+ openAppSubmitError,
+ close
+ }
+}
diff --git a/strr-platform-web/app/enums/application-type.ts b/strr-platform-web/app/enums/application-type.ts
new file mode 100644
index 000000000..65567eeb2
--- /dev/null
+++ b/strr-platform-web/app/enums/application-type.ts
@@ -0,0 +1,3 @@
+export enum ApplicationType {
+ PLATFORM = 'PLATFORM'
+}
diff --git a/strr-platform-web/app/interfaces/connect-btn-control/item-i.ts b/strr-platform-web/app/interfaces/connect-btn-control/item-i.ts
index 86f57310a..b50ba4142 100644
--- a/strr-platform-web/app/interfaces/connect-btn-control/item-i.ts
+++ b/strr-platform-web/app/interfaces/connect-btn-control/item-i.ts
@@ -7,4 +7,5 @@ export interface ConnectBtnControlItem {
loading?: boolean
variant?: string
trailing?: boolean
+ disabled?: boolean
}
diff --git a/strr-platform-web/app/interfaces/platform-application.ts b/strr-platform-web/app/interfaces/platform-application.ts
new file mode 100644
index 000000000..90092fcd1
--- /dev/null
+++ b/strr-platform-web/app/interfaces/platform-application.ts
@@ -0,0 +1,53 @@
+export interface ApiAddress {
+ country: string
+ address: string
+ addressLineTwo: string
+ city: string
+ province: string
+ postalCode: string
+}
+
+export interface ApiParty {
+ firstName: string
+ lastName: string
+ middleName: string
+ phoneNumber: string
+ extension: string
+ faxNumber: string
+ emailAddress: string
+}
+
+export interface ApiRep extends ApiParty {
+ jobTitle: string
+}
+
+export interface ApiBusinessDetails {
+ legalName: string
+ homeJurisdiction: string
+ businessNumber: string
+ consumerProtectionBCLicenceNumber: string
+ noticeOfNonComplianceEmail: string
+ noticeOfNonComplianceOptionalEmail: string
+ takeDownRequestEmail: string
+ takeDownRequestOptionalEmail: string
+ mailingAddress: ApiAddress
+ registeredOfficeOrAttorneyForServiceDetails: {
+ attorneyName: string
+ mailingAddress: ApiAddress
+ }
+}
+
+export interface ApiPlatformDetails {
+ brands: PlatBrand[]
+ listingSize: ListingSize
+}
+
+export interface PlatformApplicationPayload {
+ registration: {
+ registrationType: ApplicationType
+ completingParty: ApiParty
+ platformRepresentatives: ApiRep[]
+ businessDetails: ApiBusinessDetails
+ platformDetails: ApiPlatformDetails
+ }
+}
diff --git a/strr-platform-web/app/layouts/connect-form.vue b/strr-platform-web/app/layouts/connect-form.vue
index c6eef111e..f1a111e71 100644
--- a/strr-platform-web/app/layouts/connect-form.vue
+++ b/strr-platform-web/app/layouts/connect-form.vue
@@ -10,12 +10,12 @@
class="app-inner-container app-body"
data-testid="strr-form-layout-slot"
>
-
+
diff --git a/strr-platform-web/app/pages/platform/application.vue b/strr-platform-web/app/pages/platform/application.vue
index 50bae214d..b4220620f 100644
--- a/strr-platform-web/app/pages/platform/application.vue
+++ b/strr-platform-web/app/pages/platform/application.vue
@@ -35,6 +35,7 @@ onMounted(async () => {
const { platformDetails } = storeToRefs(useStrrPlatformDetails())
const { platformBusiness } = storeToRefs(useStrrPlatformBusiness())
+// const { getBusinessSchema } = useStrrPlatformBusiness()
watch(() => platformBusiness.value.hasCpbc, (val) => {
if (val && platFeeWv.value) {
removeFee(ConnectFeeCode.STR_PLAT_SM)
@@ -118,16 +119,65 @@ const setPreviousStep = () => {
}
}
-// something like this? need to discuss options
-// how can we set loading state on the submit and pay button?
-// i wonder if tracking the steps in a store would be useful
-const handlePlatformSubmit = () => {
- // validate each step
- // if invalid step, set prop on Review component to highlight invalid fields/section ?
- // show alert with some error text ?
+// need to cleanup the setButtonControl somehow
+const handlePlatformSubmit = async () => {
+ try {
+ // set buttons to loading state
+ setButtonControl({
+ leftButtons: [
+ {
+ action: setPreviousStep,
+ icon: 'i-mdi-chevron-left',
+ label: t('btn.back'),
+ variant: 'outline',
+ disabled: true
+ },
+ {
+ action: handlePlatformSubmit,
+ icon: 'i-mdi-chevron-right',
+ label: t('btn.submitAndPay'),
+ trailing: true,
+ loading: true
+ }
+ ],
+ rightButtons: []
+ })
+
+ // something like this but cleaner
+ // validate all forms
+ // const isCompletingPartyValid = getContactSchema(true).safeParse(completingParty.value).success
+ // const isPrimaryRepValid = getContactSchema(false).safeParse(primaryRep.value).success
+ // if (secondaryRep.value) {
+ // const isSecondaryRepValid = getContactSchema(false).safeParse(secondaryRep.value).success
+ // }
+ // const isBusDetailsValid = getBusinessSchema(
+ // platformBusiness.value.hasCpbc, platformBusiness.value.hasRegOffAtt)
+ // console.log(isCompletingPartyValid)
+
+ // if all steps valid, submit form with store function
+ await submitPlatformApplication()
+ } catch (e) {
- // if all steps valid, submit form with store function
- submitPlatformApplication()
+ } finally {
+ // set buttons back to non loading state
+ setButtonControl({
+ leftButtons: [
+ {
+ action: setPreviousStep,
+ icon: 'i-mdi-chevron-left',
+ label: t('btn.back'),
+ variant: 'outline'
+ },
+ {
+ action: handlePlatformSubmit,
+ icon: 'i-mdi-chevron-right',
+ label: t('btn.submitAndPay'),
+ trailing: true
+ }
+ ],
+ rightButtons: []
+ })
+ }
}
watch(activeStepIndex, (val) => {
diff --git a/strr-platform-web/app/plugins/strr-api.ts b/strr-platform-web/app/plugins/strr-api.ts
new file mode 100644
index 000000000..51c7425a4
--- /dev/null
+++ b/strr-platform-web/app/plugins/strr-api.ts
@@ -0,0 +1,29 @@
+export default defineNuxtPlugin(() => {
+ const strrApiUrl = useRuntimeConfig().public.strrApiURL
+ const accountStore = useConnectAccountStore()
+
+ const { $keycloak } = useNuxtApp()
+
+ const api = $fetch.create({
+ baseURL: strrApiUrl,
+ onRequest ({ options }) {
+ const headers = options.headers ||= {}
+ if (Array.isArray(headers)) {
+ headers.push(['Authorization', `Bearer ${$keycloak.token}`])
+ headers.push(['Account-Id', accountStore.currentAccount.id])
+ } else if (headers instanceof Headers) {
+ headers.set('Authorization', `Bearer ${$keycloak.token}`)
+ headers.set('Account-Id', accountStore.currentAccount.id)
+ } else {
+ headers.Authorization = `Bearer ${$keycloak.token}`
+ headers['Account-Id'] = accountStore.currentAccount.id
+ }
+ }
+ })
+
+ return {
+ provide: {
+ strrApi: api
+ }
+ }
+})
diff --git a/strr-platform-web/app/stores/platformApplication.ts b/strr-platform-web/app/stores/platformApplication.ts
index d3a0a9e5c..290648204 100644
--- a/strr-platform-web/app/stores/platformApplication.ts
+++ b/strr-platform-web/app/stores/platformApplication.ts
@@ -1,31 +1,59 @@
export const useStrrPlatformApplication = defineStore('strr/platformApplication', () => {
+ const { $strrApi } = useNuxtApp()
const { completingParty, primaryRep, secondaryRep } = storeToRefs(useStrrPlatformContact())
const { platformBusiness } = storeToRefs(useStrrPlatformBusiness())
const { platformDetails } = storeToRefs(useStrrPlatformDetails())
+ const strrModal = useStrrModals()
const confirmInfoAccuracy = ref(false)
const confirmDelistAndCancelBookings = ref(false)
- const getPlatformApplication = () => {
- return {
- completingParty: completingParty.value,
- primaryRep: primaryRep.value,
- secondaryRep: secondaryRep.value,
- platformBusiness: platformBusiness.value,
- platformDetails: platformDetails.value
+ function createApplicationBody (): PlatformApplicationPayload {
+ const applicationBody: PlatformApplicationPayload = {
+ registration: {
+ registrationType: ApplicationType.PLATFORM,
+ completingParty: formatParty(completingParty.value),
+ platformRepresentatives: [],
+ businessDetails: formatBusinessDetails(platformBusiness.value),
+ platformDetails: formatPlatformDetails(platformDetails.value)
+ }
}
+
+ if (primaryRep.value !== undefined) {
+ applicationBody.registration.platformRepresentatives.push(
+ formatRepresentative(primaryRep.value)
+ )
+ }
+
+ if (secondaryRep.value !== undefined) {
+ applicationBody.registration.platformRepresentatives.push(
+ formatRepresentative(secondaryRep.value)
+ )
+ }
+
+ return applicationBody
}
- // TODO: submit
- function submitPlatformApplication () {
- // eslint-disable-next-line
- console.log('submitting platform app')
+ async function submitPlatformApplication () {
+ // validate all forms/fields first??
+ try {
+ const body = createApplicationBody()
+
+ // console.log('submitting application: ', body)
+ await $strrApi('/applications', {
+ method: 'POST',
+ body
+ })
+ } catch (e) {
+ logFetchError(e, 'Error creating platform application')
+ strrModal.openAppSubmitError() // pass in error object ??
+ }
}
return {
confirmInfoAccuracy,
confirmDelistAndCancelBookings,
- getPlatformApplication,
+ // getPlatformApplication,
submitPlatformApplication
}
})
diff --git a/strr-platform-web/app/utils/platform-application.ts b/strr-platform-web/app/utils/platform-application.ts
new file mode 100644
index 000000000..ed60e7ab5
--- /dev/null
+++ b/strr-platform-web/app/utils/platform-application.ts
@@ -0,0 +1,65 @@
+export function formatPhoneNumber (phone: ConnectPhone) {
+ return {
+ phoneNumber: `${phone.countryCode ?? ''}${phone.number}`,
+ extension: phone.extension ?? ''
+ }
+}
+
+export function formatAddress (add: ConnectAddress): ApiAddress {
+ return {
+ country: add.country,
+ address: add.street,
+ addressLineTwo: add.streetAdditional,
+ city: add.city,
+ province: add.region,
+ postalCode: add.postalCode
+ }
+}
+
+export function formatParty (party: Contact): ApiParty {
+ return {
+ firstName: party.firstName,
+ middleName: party.middleName ?? '',
+ lastName: party.lastName,
+ ...formatPhoneNumber(party.phone),
+ faxNumber: party.faxNumber ?? '',
+ emailAddress: party.emailAddress
+ }
+}
+
+export function formatRepresentative (rep: PlatformContact): ApiRep {
+ return {
+ ...formatParty(rep),
+ jobTitle: rep.position
+ }
+}
+
+export function formatBusinessDetails (bus: PlatBusiness): ApiBusinessDetails {
+ return {
+ legalName: bus.legalName,
+ homeJurisdiction: bus.homeJurisdiction,
+ businessNumber: bus.businessNumber,
+ consumerProtectionBCLicenceNumber: bus.cpbcLicenceNumber,
+ noticeOfNonComplianceEmail: bus.nonComplianceEmail,
+ noticeOfNonComplianceOptionalEmail: bus.nonComplianceEmailOptional,
+ takeDownRequestEmail: bus.takeDownEmail,
+ takeDownRequestOptionalEmail: bus.takeDownEmailOptional,
+ mailingAddress: formatAddress(bus.mailingAddress),
+ registeredOfficeOrAttorneyForServiceDetails: {
+ attorneyName: bus.regOfficeOrAtt.attorneyName,
+ mailingAddress: formatAddress(bus.regOfficeOrAtt.mailingAddress)
+ }
+ }
+}
+
+export function formatPlatformDetails (
+ plat: { brands: PlatBrand[], listingSize: ListingSize | undefined }
+): ApiPlatformDetails {
+ return {
+ brands: plat.brands.map(brand => ({
+ name: brand.name,
+ website: brand.website
+ })),
+ listingSize: plat.listingSize! // should never be undefined after being validated
+ }
+}