From 7b0a1fda03513a5745b965ae1810a55a40d2bf0c Mon Sep 17 00:00:00 2001
From: deetz99 <dietrich@wolpert.ca>
Date: Thu, 16 Jan 2025 14:33:14 -0800
Subject: [PATCH] simplify expansion handling

---
 strr-base-web/app/app.vue                     |  9 +++
 .../app/components/connect/ExpansionRoot.vue  | 20 ++++++
 .../app/composables/useStrrExpansion.ts       | 63 ++++++++++++++++++
 strr-base-web/app/types/strr-expansion.ts     | 13 ++++
 .../app/components/Host/SubHeader.vue         | 17 +++--
 .../app/components/NavigationTabs.vue         | 65 -------------------
 .../app/composables/useHostExpansion.ts       | 30 +++++++++
 .../app/pages/examine/[applicationId].vue     | 23 +++----
 .../app/types/host-details-display-item.ts    |  1 -
 strr-examiner-web/package.json                |  2 +-
 10 files changed, 159 insertions(+), 84 deletions(-)
 create mode 100644 strr-base-web/app/components/connect/ExpansionRoot.vue
 create mode 100644 strr-base-web/app/composables/useStrrExpansion.ts
 create mode 100644 strr-base-web/app/types/strr-expansion.ts
 delete mode 100644 strr-examiner-web/app/components/NavigationTabs.vue
 create mode 100644 strr-examiner-web/app/composables/useHostExpansion.ts
 delete mode 100644 strr-examiner-web/app/types/host-details-display-item.ts

diff --git a/strr-base-web/app/app.vue b/strr-base-web/app/app.vue
index 892e9a40..b38189ef 100644
--- a/strr-base-web/app/app.vue
+++ b/strr-base-web/app/app.vue
@@ -1,4 +1,6 @@
 <script setup lang="ts">
+import { expansionInjectionKey } from '~/composables/useStrrExpansion'
+
 const i18nHead = useLocaleHead({
   addDirAttribute: true,
   addSeoAttributes: true
@@ -12,6 +14,13 @@ useHead({
   }
 })
 
+// provide initial expansion state
+const initialExpansionState = shallowRef<ExpansionState>({
+  component: 'div',
+  props: {}
+})
+provide(expansionInjectionKey, initialExpansionState)
+
 onMounted(async () => {
   const msgConfig = useAppConfig().strrBaseLayer.sbcWebMsg
 
diff --git a/strr-base-web/app/components/connect/ExpansionRoot.vue b/strr-base-web/app/components/connect/ExpansionRoot.vue
new file mode 100644
index 00000000..a18bf128
--- /dev/null
+++ b/strr-base-web/app/components/connect/ExpansionRoot.vue
@@ -0,0 +1,20 @@
+<script lang="ts" setup>
+import { useStrrExpansion, expansionInjectionKey } from '~/composables/useStrrExpansion'
+
+const expansionState = inject(expansionInjectionKey)
+
+const { isExpanded } = useStrrExpansion()
+
+// This will currently only work with 1 component instance at a time (per page)
+</script>
+<template>
+  <ConnectTransitionCollapse>
+    <div v-if="isExpanded">
+      <component
+        :is="expansionState.component"
+        v-if="expansionState"
+        v-bind="expansionState.props"
+      />
+    </div>
+  </ConnectTransitionCollapse>
+</template>
diff --git a/strr-base-web/app/composables/useStrrExpansion.ts b/strr-base-web/app/composables/useStrrExpansion.ts
new file mode 100644
index 00000000..4dee6424
--- /dev/null
+++ b/strr-base-web/app/composables/useStrrExpansion.ts
@@ -0,0 +1,63 @@
+import type { ShallowRef, Component } from 'vue'
+import { createSharedComposable } from '@vueuse/core'
+import type { ExpansionState, Expansion, ComponentProps } from '~/types/strr-expansion'
+
+export const expansionInjectionKey: InjectionKey<ShallowRef<ExpansionState>> = Symbol('strr-examiner-expansion')
+
+function _useExpansion () {
+  const expansionState = inject(expansionInjectionKey)
+
+  const isExpanded = ref(false)
+
+  function open<T extends Component> (component: T, props?: Expansion & ComponentProps<T>) {
+    if (!expansionState) {
+      throw new Error('useExpansion() is called without provider')
+    }
+    // Set the shared expansion state
+    expansionState.value = {
+      component,
+      props: props ?? {}
+    }
+    isExpanded.value = true
+  }
+
+  function close () {
+    if (!expansionState?.value) { return }
+
+    isExpanded.value = false
+    reset()
+  }
+
+  function reset () {
+    if (!expansionState?.value) { return }
+
+    expansionState.value = {
+      component: 'div',
+      props: {} as Expansion & ComponentProps<any>
+    }
+  }
+
+  // TODO: implement ?
+  /**
+   * Update the expansion props partially.
+   */
+  // function patch<T extends Component = {}>(props: Partial<Expansion & ComponentProps<T>>) {
+  //   expansionState.value = {
+  //     ...expansionState.value,
+  //     props: {
+  //       ...expansionState.value.props,
+  //       ...props
+  //     }
+  //   }
+  // }
+
+  return {
+    open,
+    close,
+    reset,
+    // patch,
+    isExpanded
+  }
+}
+
+export const useStrrExpansion = createSharedComposable(_useExpansion)
diff --git a/strr-base-web/app/types/strr-expansion.ts b/strr-base-web/app/types/strr-expansion.ts
new file mode 100644
index 00000000..ef9ad2fe
--- /dev/null
+++ b/strr-base-web/app/types/strr-expansion.ts
@@ -0,0 +1,13 @@
+export type ComponentProps<T> =
+T extends new () => { $props: infer P } ? NonNullable<P> :
+  T extends (props: infer P, ...args: any) => any ? P :
+      {}
+
+export interface Expansion {
+  onClose?: () => void
+}
+
+export interface ExpansionState {
+  component: Component | string
+  props: Expansion
+}
diff --git a/strr-examiner-web/app/components/Host/SubHeader.vue b/strr-examiner-web/app/components/Host/SubHeader.vue
index 3ea7151c..c2217fef 100644
--- a/strr-examiner-web/app/components/Host/SubHeader.vue
+++ b/strr-examiner-web/app/components/Host/SubHeader.vue
@@ -1,11 +1,13 @@
 <script setup lang="ts">
 const props = defineProps<{ application: HostApplicationResp }>()
 const reg = props.application.registration
-defineEmits<{
-  openExpansion: [OpenExpansionEvent]
-}>()
+// defineEmits<{
+//   openExpansion: [OpenExpansionEvent]
+// }>()
 
 const { t } = useI18n()
+
+const hostExp = useHostExpansion()
 </script>
 <template>
   <div class="app-inner-container">
@@ -34,8 +36,9 @@ const { t } = useI18n()
             :label="displayContactFullName(reg.primaryContact!)"
             :padded="false"
             variant="link"
-            @click="$emit('openExpansion', [ExpansionItem.HOST_OWNERS, { display: 'primaryContact' }])"
+            @click="hostExp.openHostOwners(application, 'primaryContact')"
           />
+          <!-- @click="$emit('openExpansion', [ExpansionItem.HOST_OWNERS, { display: 'primaryContact' }])" -->
         </div>
         <div>
           <UIcon name="i-mdi-at" />
@@ -84,8 +87,9 @@ const { t } = useI18n()
               : reg?.secondaryContact?.businessLegalName"
             :padded="false"
             variant="link"
-            @click="$emit('openExpansion', [ExpansionItem.HOST_OWNERS, { display: 'secondaryContact' }])"
+            @click="hostExp.openHostOwners(application, 'secondaryContact')"
           />
+          <!-- @click="$emit('openExpansion', [ExpansionItem.HOST_OWNERS, { display: 'secondaryContact' }])" -->
         </div>
 
         <div v-if="reg?.propertyManager?.propertyManagerType" class="flex items-center gap-1">
@@ -96,8 +100,9 @@ const { t } = useI18n()
               : reg?.propertyManager?.business?.legalName"
             :padded="false"
             variant="link"
-            @click="$emit('openExpansion', [ExpansionItem.HOST_OWNERS, { display: 'propertyManager' }])"
+            @click="hostExp.openHostOwners(application, 'propertyManager')"
           />
+          <!-- @click="$emit('openExpansion', [ExpansionItem.HOST_OWNERS, { display: 'propertyManager' }])" -->
         </div>
       </div>
     </div>
diff --git a/strr-examiner-web/app/components/NavigationTabs.vue b/strr-examiner-web/app/components/NavigationTabs.vue
deleted file mode 100644
index 0d8dd7f5..00000000
--- a/strr-examiner-web/app/components/NavigationTabs.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<script setup lang="ts">
-import { RoutesE } from '~/enums/routes'
-
-const { isAuthenticated } = useKeycloak()
-
-const localePath = useLocalePath()
-const { loading } = storeToRefs(useConnectDetailsHeaderStore())
-const { getNextApplication } = useExaminerStore()
-const route = useRoute()
-
-const activeTab = computed(() => route.path.includes(RoutesE.EXAMINE) ? 0 : 1)
-
-async function onChange (index: number) {
-  loading.value = true
-  const item = items[index]
-
-  let navigateToPath = item.path as string
-
-  if (item.path.includes(RoutesE.EXAMINE)) {
-    const nextApp = await getNextApplication()
-    navigateToPath = `${item?.path}/${nextApp}`
-  }
-
-  return navigateTo(localePath(navigateToPath))
-}
-
-const items = [
-  {
-    key: 'examine',
-    label: 'Examine',
-    path: RoutesE.EXAMINE
-  },
-  {
-    key: 'dashboard',
-    label: 'Search',
-    path: RoutesE.DASHBOARD
-  }]
-
-</script>
-
-<template>
-  <UTabs
-    v-if="isAuthenticated"
-    :items="items"
-    :ui="{
-      wrapper: 'relative h-full space-y-0',
-      list: {
-        background: '',
-        marker: {
-          background: ''
-        },
-        tab: {
-          background: 'bg-transparent',
-          active: 'text-white bg-transparent underline underline-offset-4 font-semibold',
-        }
-      }
-    }"
-    :model-value="activeTab"
-    @change="onChange"
-  />
-</template>
-
-<style scoped>
-
-</style>
diff --git a/strr-examiner-web/app/composables/useHostExpansion.ts b/strr-examiner-web/app/composables/useHostExpansion.ts
new file mode 100644
index 00000000..1b3b4eb8
--- /dev/null
+++ b/strr-examiner-web/app/composables/useHostExpansion.ts
@@ -0,0 +1,30 @@
+// https://ui.nuxt.com/components/modal#control-programmatically
+import {
+  HostExpansionOwners
+} from '#components'
+
+export const useHostExpansion = () => {
+  const exp = useStrrExpansion()
+
+  function openHostOwners (
+    application: HostApplicationResp,
+    display: 'primaryContact' | 'secondaryContact' | 'propertyManager'
+  ) {
+    exp.open(HostExpansionOwners, {
+      application,
+      display,
+      onClose () {
+        exp.close()
+      }
+    })
+  }
+
+  function close () {
+    exp.close()
+  }
+
+  return {
+    openHostOwners,
+    close
+  }
+}
diff --git a/strr-examiner-web/app/pages/examine/[applicationId].vue b/strr-examiner-web/app/pages/examine/[applicationId].vue
index 6bf40af0..4f22b148 100644
--- a/strr-examiner-web/app/pages/examine/[applicationId].vue
+++ b/strr-examiner-web/app/pages/examine/[applicationId].vue
@@ -17,15 +17,15 @@ definePageMeta({
 })
 
 const initialMount = ref(true) // flag for whether to fetch next or specific application on mount - true until initial application is loaded
-const showExpansion = ref<boolean>(false) // show/hide expansion items
-const expansionItem = ref<ExpansionItem | undefined>(undefined) // expansion component to display
-const expansionProps: Ref<Record<string, unknown>> = ref({}) // any props passed to expansion components
+// const showExpansion = ref<boolean>(false) // show/hide expansion items
+// const expansionItem = ref<ExpansionItem | undefined>(undefined) // expansion component to display
+// const expansionProps: Ref<Record<string, unknown>> = ref({}) // any props passed to expansion components
 
-const manageExpansion = (e: OpenExpansionEvent) => {
-  expansionItem.value = e[0] // set expansion component to open
-  expansionProps.value = e[1] // bind any custom props
-  showExpansion.value = true
-}
+// const manageExpansion = (e: OpenExpansionEvent) => {
+//   expansionItem.value = e[0] // set expansion component to open
+//   expansionProps.value = e[1] // bind any custom props
+//   showExpansion.value = true
+// }
 
 // TODO: fix typing
 const { data, status, error, refresh } = await useAsyncData<HousApplicationResponse>(
@@ -123,18 +123,19 @@ watch(
         <HostSubHeader
           v-if="application?.registration.registrationType === ApplicationType.HOST"
           :application
-          @open-expansion="manageExpansion"
         />
+        <!-- @open-expansion="manageExpansion" -->
       </div>
 
       <div class="app-inner-container space-y-10 py-10">
-        <ExpansionContainer
+        <ConnectExpansionRoot />
+        <!-- <ExpansionContainer
           v-model="showExpansion"
           v-bind="expansionProps"
           :application
           :expansion-item="expansionItem"
           @close="showExpansion = false"
-        />
+        /> -->
 
         <!-- TODO: other supporting info -->
         <HostSupportingInfo
diff --git a/strr-examiner-web/app/types/host-details-display-item.ts b/strr-examiner-web/app/types/host-details-display-item.ts
deleted file mode 100644
index f4a4fe76..00000000
--- a/strr-examiner-web/app/types/host-details-display-item.ts
+++ /dev/null
@@ -1 +0,0 @@
-export type HostDetailsDisplayItem = 'primaryContact' | 'secondaryContact' | 'propertyManager' | undefined
diff --git a/strr-examiner-web/package.json b/strr-examiner-web/package.json
index 3cea2535..c297319f 100644
--- a/strr-examiner-web/package.json
+++ b/strr-examiner-web/package.json
@@ -2,7 +2,7 @@
   "name": "strr-examiner-web",
   "private": true,
   "type": "module",
-  "version": "0.0.8",
+  "version": "0.0.9",
   "scripts": {
     "build-check": "nuxt build",
     "build": "nuxt generate",