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

hosts drafts #442

Merged
merged 6 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 6 additions & 5 deletions strr-base-web/app/components/connect/ButtonControl.vue
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

with 3 extra buttons I needed to update the styling for smaller screens

example:
Screenshot 2025-01-08 at 3 36 05 PM
Screenshot 2025-01-08 at 3 35 44 PM

Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ const rightButtons = computed(() => buttonControl.value?.rightButtons || [])
<template>
<div class="bg-white py-10" data-testid="button-control">
<div class="app-inner-container">
<div class="grid grid-cols-2">
<div v-if="leftButtons.length > 0" class="col-start-1">
<div class="flex gap-4">
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<div v-if="leftButtons.length > 0">
<div class="flex justify-center gap-4 md:justify-start">
<UButton
v-for="(button, i) in leftButtons"
:key="'left-button-' + i"
Expand All @@ -28,15 +28,16 @@ const rightButtons = computed(() => buttonControl.value?.rightButtons || [])
/>
</div>
</div>
<div class="col-span-1 col-start-2">
<div v-if="rightButtons.length > 0" class="flex justify-end gap-4">
<div v-if="rightButtons.length > 0" class="col-span-1">
<div class="flex justify-center gap-4 md:justify-end">
<UButton
v-for="(button, i) in rightButtons"
:key="'right-button-' + i"
class="max-w-fit px-7 py-3"
:class="button.class"
block
:color="button.color || 'primary'"
:disabled="button.disabled || false"
:icon="button.icon || ''"
:label="button.label"
:loading="button.loading || false"
Expand Down
14 changes: 8 additions & 6 deletions strr-base-web/app/components/todo/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ defineProps<{
</p>
</slot>
</div>
<UButton
:label="button.label"
:color="button.colour || 'primary'"
:icon="button.icon"
@click="button.action()"
/>
<div>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

wrapped the button in a div to prevent it growing with the width of the row

<UButton
:label="button.label"
:color="button.colour || 'primary'"
:icon="button.icon"
@click="button.action()"
/>
</div>
</div>
<slot />
</div>
Expand Down
12 changes: 9 additions & 3 deletions strr-base-web/app/composables/useStrrApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,15 @@ export const useStrrApi = () => {
return resp.applications
}

const postApplication = async <T extends { registration: any }, R extends T>(body: T) => {
return await $strrApi<R>('/applications', {
method: 'POST',
const postApplication = async <T extends { registration: any }, R extends T>(
body: T,
isDraft = false,
applicationId?: string
) => {
const path = applicationId ? `/applications/${applicationId}` : '/applications'
return await $strrApi<R>(path, {
method: applicationId ? 'PUT' : 'POST',
headers: (isDraft ? { isDraft: true } : {}) as HeadersInit,
body
})
}
Expand Down
1 change: 1 addition & 0 deletions strr-base-web/app/locales/en-CA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default {
save: 'Save',
saveExit: 'Save and Resume Later',
beginApplication: 'Begin Application',
resume: 'Resume',
resumeApplication: 'Resume Application',
acceptTos: {
main: 'Accept Terms of Use',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ export function setButtonControl (buttonControl: ConnectBtnControl) {
const route = useRoute()
route.meta.buttonControl = buttonControl
}

export function getButtonControl (): ConnectBtnControl {
const route = useRoute()
return route.meta.buttonControl as ConnectBtnControl
}
98 changes: 59 additions & 39 deletions strr-host-pm-web/app/pages/application.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { ConnectStepper, FormReview } from '#components'

const { t } = useI18n()
const route = useRoute()
const localePath = useLocalePath()
const strrModal = useStrrModals()
const { handlePaymentRedirect } = useConnectNav()
Expand All @@ -16,6 +17,9 @@ const {
validateUserConfirmation,
$reset: applicationReset
} = useHostApplicationStore()
const permitStore = useHostPermitStore()

const applicationId = route.query.applicationId as string

// fee stuff
const {
Expand All @@ -33,8 +37,11 @@ const hostFee2 = ref<ConnectFeeItem | undefined>(undefined)
const hostFee3 = ref<ConnectFeeItem | undefined>(undefined)

onMounted(async () => {
// TODO: check for application id in the route query, if there then load the application
// loading.value = true
kialj876 marked this conversation as resolved.
Show resolved Hide resolved
applicationReset()
if (applicationId) {
await permitStore.loadHostData(applicationId, true)
}
const [fee1, fee2] = await Promise.all([
getFee(StrrFeeEntityType.STRR, StrrFeeCode.STR_HOST_1),
getFee(StrrFeeEntityType.STRR, StrrFeeCode.STR_HOST_2)
Expand All @@ -46,6 +53,7 @@ onMounted(async () => {
if (hostFee1.value) {
setPlaceholderServiceFee(hostFee1.value.serviceFees)
}
// loading.value = false
})

const setFeeBasedOnProperty = () => {
Expand Down Expand Up @@ -125,31 +133,52 @@ const activeStep = ref<Step>(steps.value[activeStepIndex.value] as Step)
const stepperRef = shallowRef<InstanceType<typeof ConnectStepper> | null>(null)
const reviewFormRef = shallowRef<InstanceType<typeof FormReview> | null>(null)

// TODO: move button management into composable ?
const handleButtonLoading = (reset: boolean, buttonGrp?: 'left' | 'right', buttonIndex?: number) => {
kialj876 marked this conversation as resolved.
Show resolved Hide resolved
// set button control for loading / disabling buttons on submit or save or reset to default
const updateButtonGrp = (buttonArray: ConnectBtnControlItem[], grp: 'left' | 'right') => {
for (const [index, element] of buttonArray.entries()) {
if (reset) {
element.disabled = false
element.loading = false
} else {
element.loading = (grp === buttonGrp) && index === buttonIndex
element.disabled = !element.loading
}
}
}
const buttonControl = getButtonControl()
// update left buttons with loading / disabled as required
updateButtonGrp(buttonControl.leftButtons, 'left')
// update right buttons with loading / disabled as required
updateButtonGrp(buttonControl.rightButtons, 'right')
}

const saveApplication = async (resumeLater = false) => {
handleButtonLoading(false, 'left', resumeLater ? 1 : 2)
// prevent flicker of buttons by waiting half a second
try {
await Promise.all([
new Promise(resolve => setTimeout(resolve, 500)),
submitApplication(true, applicationId)
])
if (resumeLater) {
await navigateTo(localePath('/dashboard'))
}
} catch (e) {
logFetchError(e, 'Error saving host application')
strrModal.openAppSubmitError(e)
} finally {
handleButtonLoading(true)
}
}

// need to cleanup the setButtonControl somehow
const handleSubmit = async () => {
let formErrors: MultiFormValidationResult = []
try {
// TODO: move button management into composable ?
// set buttons to loading state
setButtonControl({
leftButtons: [],
rightButtons: [
{
action: () => undefined, // is disabled
icon: 'i-mdi-chevron-left',
label: t('btn.back'),
variant: 'outline',
disabled: true
},
{
action: () => undefined, // is disabled
icon: 'i-mdi-chevron-right',
label: t('btn.submitAndPay'),
trailing: true,
loading: true
}
]
})
handleButtonLoading(false, 'right', 1)

activeStep.value.complete = true // set final review step as active before validation
reviewFormRef.value?.validateConfirmation() // validate confirmation checkboxes on submit
Expand Down Expand Up @@ -195,23 +224,7 @@ const handleSubmit = async () => {
strrModal.openAppSubmitError(e)
} finally {
// set buttons back to non loading state
setButtonControl({
leftButtons: [],
rightButtons: [
{
action: () => stepperRef.value?.setPreviousStep(),
icon: 'i-mdi-chevron-left',
label: t('btn.back'),
variant: 'outline'
},
{
action: handleSubmit,
icon: 'i-mdi-chevron-right',
label: t('btn.submitAndPay'),
trailing: true
}
]
})
handleButtonLoading(true)
}
}

Expand All @@ -234,7 +247,14 @@ watch(activeStepIndex, (val) => {
trailing: true
})

setButtonControl({ leftButtons: [], rightButtons: buttons })
setButtonControl({
leftButtons: [
{ action: () => navigateTo(localePath('/dashboard')), label: t('btn.cancel'), variant: 'outline' },
{ action: () => saveApplication(true), label: t('btn.saveExit'), variant: 'outline' },
{ action: saveApplication, label: t('btn.save'), variant: 'outline' }
],
rightButtons: buttons
})
}, { immediate: true })

// remove unnecessary docs when/if exemption options change
Expand Down
54 changes: 40 additions & 14 deletions strr-host-pm-web/app/pages/dashboard/index.vue
Copy link
Collaborator Author

@kialj876 kialj876 Jan 8, 2025

Choose a reason for hiding this comment

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

examples of new buttons / styling on big + small screens:
big screen
Screenshot 2025-01-08 at 3 39 35 PM
drop down
Screenshot 2025-01-08 at 3 44 12 PM
smaller screen
Screenshot 2025-01-08 at 3 40 24 PM

Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,16 @@ const { data: hostPmList, status } = await useAsyncData(
}
)

function isDraft (status: string) {
return status === 'Draft'
}

async function handleItemSelect (row: any) {
await navigateTo(localePath('/dashboard/' + row.applicationNumber))
if (isDraft(row.status)) {
await navigateTo(localePath('/application?applicationId=' + row.applicationNumber))
} else {
await navigateTo(localePath('/dashboard/' + row.applicationNumber))
}
}
</script>
<template>
Expand Down Expand Up @@ -176,19 +184,37 @@ async function handleItemSelect (row: any) {
</template>

<template #actions-data="{ row }">
<UButton
:label="$t('btn.view')"
:aria-label="
$t('btn.ariaViewDetails', {
name: row.name,
address: `${row.address.unitNumber
? row.address.unitNumber + '-'
: ''}${row.address.streetNumber} ${row.address.streetName}, ${row.address.city}`
})
"
:block="true"
@click="handleItemSelect(row)"
/>
<div class="flex flex-col gap-px lg:flex-row">
<UButton
:class="isDraft(row.status) ? 'justify-center grow lg:rounded-r-none' : ''"
:label="isDraft(row.status) ? $t('label.resumeDraft') : $t('btn.view')"
:aria-label="
kialj876 marked this conversation as resolved.
Show resolved Hide resolved
$t('btn.ariaViewDetails', {
name: row.name,
address: `${row.address.unitNumber
? row.address.unitNumber + '-'
: ''}${row.address.streetNumber} ${row.address.streetName}, ${row.address.city}`
})
"
:block="!isDraft(row.status)"
@click="handleItemSelect(row)"
/>
<UPopover v-if="isDraft(row.status)" :popper="{ placement: 'bottom-end' }">
<UButton
class="grow justify-center lg:flex-none lg:rounded-l-none"
icon="i-mdi-menu-down"
:aria-label="$t('text.showMoreOptions')"
/>
<template #panel>
<UButton
class="m-2"
:label="$t('word.Delete')"
variant="link"
@click="console.log('delete', row.applicationNumber)"
/>
</template>
</UPopover>
</div>
</template>
</UTable>
</ConnectPageSection>
Expand Down
9 changes: 6 additions & 3 deletions strr-host-pm-web/app/stores/hostApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,15 @@ export const useHostApplicationStore = defineStore('host/application', () => {
}
}

const submitApplication = async () => {
const submitApplication = async (isDraft = false, applicationId?: string) => {
const body = createApplicationBody()

// console.info('submitting application: ', body)

const res = await postApplication<HostApplicationPayload, HostApplicationResp>(body) as HostApplicationResp
const res = await postApplication<HostApplicationPayload, HostApplicationResp>(
body,
isDraft,
applicationId
) as HostApplicationResp

const paymentToken = res.header.paymentToken
const filingId = res.header.applicationNumber
Expand Down
14 changes: 10 additions & 4 deletions strr-host-pm-web/app/stores/hostPermit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const useHostPermitStore = defineStore('host/permit', () => {
const documentStore = useDocumentStore()
const { hostOwners } = storeToRefs(ownerStore)
const { blInfo, unitAddress, unitDetails } = storeToRefs(propertyStore)
const { prRequirements, propertyReqs } = storeToRefs(propertyReqStore)
const { prRequirements, propertyReqs, showUnitDetailsForm } = storeToRefs(propertyReqStore)
const { storedDocuments } = storeToRefs(documentStore)

const {
Expand All @@ -25,10 +25,10 @@ export const useHostPermitStore = defineStore('host/permit', () => {
downloadRegistrationCert
} = useStrrBasePermit<HostRegistrationResp, HostApplicationResp, ApiHostApplication>()

const loadHostData = async (applicationId: string) => {
const loadHostData = async (applicationId: string, loadDraft = false) => {
$reset()
await loadPermitData(applicationId)
if (showPermitDetails.value) {
if (showPermitDetails.value || loadDraft) {
// set sub store values
hostOwners.value.push(formatOwnerHostUI(
permitDetails.value.primaryContact,
Expand All @@ -43,10 +43,16 @@ export const useHostPermitStore = defineStore('host/permit', () => {
unitDetails.value = formatHostUnitDetailsUI(permitDetails.value.unitDetails)
blInfo.value = formatHostUnitDetailsBlInfoUI(permitDetails.value.unitDetails)
unitAddress.value = { address: formatHostUnitAddressUI(permitDetails.value.unitAddress) }
showUnitDetailsForm.value = !!unitAddress.value
prRequirements.value.isPropertyPrExempt = !!permitDetails.value.unitDetails.prExemptReason
prRequirements.value.prExemptionReason = permitDetails.value.unitDetails.prExemptReason
if (application.value?.registration.strRequirements) {
if (application.value?.registration.strRequirements && unitAddress.value?.address?.streetName) {
propertyReqs.value = application.value?.registration.strRequirements
if (Object.keys(application.value.registration.strRequirements).length === 0) {
// run the requirements check again in case it has errors (errors are not saved by the api)
showUnitDetailsForm.value = false
await propertyReqStore.getPropertyReqs()
}
}
storedDocuments.value = permitDetails.value.documents?.map<UiDocument>(val => ({
file: {} as File,
Expand Down
Loading
Loading