From d01c484f689522bd4f00b308fdb55a8a3c402833 Mon Sep 17 00:00:00 2001 From: Karim El Jazzar Date: Mon, 16 Mar 2026 13:48:43 -0700 Subject: [PATCH 1/7] feat(ui+api): added new registration sub-status column --- strr-api/pyproject.toml | 2 +- strr-api/src/strr_api/models/dataclass.py | 2 +- strr-api/src/strr_api/models/rental.py | 41 ++++--- .../src/strr_api/resources/registrations.py | 10 +- .../unit/resources/test_registrations.py | 40 ++++--- .../useExaminerDashboardPersistence.ts | 19 ++-- strr-examiner-web/app/pages/dashboard.vue | 100 +++++++++++++++--- strr-examiner-web/app/stores/examiner.ts | 57 ++++++++-- strr-examiner-web/package.json | 3 +- .../tests/unit/dashboard.spec.ts | 6 ++ ...use-examiner-dashboard-persistence.spec.ts | 10 +- 11 files changed, 211 insertions(+), 79 deletions(-) diff --git a/strr-api/pyproject.toml b/strr-api/pyproject.toml index 50464a8bf..0563a38bb 100644 --- a/strr-api/pyproject.toml +++ b/strr-api/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "strr-api" -version = "0.3.11" +version = "0.3.12" description = "" authors = ["thorwolpert "] license = "BSD 3-Clause" diff --git a/strr-api/src/strr_api/models/dataclass.py b/strr-api/src/strr_api/models/dataclass.py index f0aada12d..b9bab320f 100644 --- a/strr-api/src/strr_api/models/dataclass.py +++ b/strr-api/src/strr_api/models/dataclass.py @@ -76,4 +76,4 @@ class RegistrationSearch: noc_statuses: List[str] | None = None is_set_aside: bool | None = None local_gov: str | None = None - renewals_only: bool | None = None + review_renew: bool | None = None diff --git a/strr-api/src/strr_api/models/rental.py b/strr-api/src/strr_api/models/rental.py index 2c85dcad5..e8c3bdf40 100644 --- a/strr-api/src/strr_api/models/rental.py +++ b/strr-api/src/strr_api/models/rental.py @@ -141,18 +141,23 @@ def search_registrations(cls, filter_criteria: RegistrationSearch): ) if filter_criteria.requirements: query = cls._filter_by_registration_requirement(filter_criteria.requirements, query) + sub_status_conditions = [] if filter_criteria.approval_methods: - query = cls._filter_by_approval_method(filter_criteria.approval_methods, query) + sub_status_conditions.append(cls._approval_method_condition(filter_criteria.approval_methods)) if filter_criteria.noc_statuses: - query = query.filter(Registration.noc_status.in_(filter_criteria.noc_statuses)) + sub_status_conditions.append(Registration.noc_status.in_(filter_criteria.noc_statuses)) if filter_criteria.is_set_aside is True: - query = query.filter(Registration.is_set_aside == True) # noqa: E712 + sub_status_conditions.append(Registration.is_set_aside == True) # noqa: E712 if filter_criteria.local_gov: query = query.join(RentalProperty).filter( RentalProperty.jurisdiction.ilike(f"%{filter_criteria.local_gov}%") ) - if filter_criteria.renewals_only is True: - query = cls._filter_by_renewals_only(query) + if filter_criteria.review_renew is True: + sub_status_conditions.append(cls._review_renew_condition()) + if sub_status_conditions: + # Sub-status filters are combined with OR so examiners can select + # multiple sub-status categories in a single search. + query = query.filter(db.or_(*sub_status_conditions)) sort_column = getattr(Registration, filter_criteria.sort_by, Registration.id) if filter_criteria.sort_order and filter_criteria.sort_order.lower() == "asc": query = query.order_by(sort_column.asc()) @@ -436,15 +441,10 @@ def _filter_by_registration_requirement(cls, requirement: list[str], query): return query @classmethod - def _filter_by_approval_method(cls, approval_methods: list[str], query): - """Filter registrations by application approval method. - - Only considers the most recent application (index 0 when sorted by - application_date desc) for each registration. Returns registrations - where that most recent application's status is in the given approval methods. - """ + def _approval_method_condition(cls, approval_methods: list[str]): + """Build SQL condition for filtering by latest application status.""" if not approval_methods: - return query + return None # pylint: disable=import-outside-toplevel from sqlalchemy import select @@ -458,29 +458,28 @@ def _filter_by_approval_method(cls, approval_methods: list[str], query): .limit(1) .scalar_subquery() ) - return query.filter(latest_app_status_subq.in_(approval_methods)) + return latest_app_status_subq.in_(approval_methods) @classmethod - def _filter_by_renewals_only(cls, query): - """Filter to registrations that have at least one renewal application in an approved/provisional status.""" + def _review_renew_condition(cls): + """Build SQL condition for registrations requiring renewal review.""" # pylint: disable=import-outside-toplevel from strr_api.enums.enum import ApplicationType from strr_api.models.application import Application - renewal_approved_statuses = [ - Application.Status.FULL_REVIEW_APPROVED.value, + renewal_not_fully_approved_statuses = [ + Application.Status.FULL_REVIEW.value, Application.Status.PROVISIONALLY_APPROVED.value, Application.Status.PROVISIONAL_REVIEW.value, - Application.Status.AUTO_APPROVED.value, ] renewal_exists = db.exists().where( db.and_( Application.registration_id == Registration.id, Application.type == ApplicationType.RENEWAL.value, - Application.status.in_(renewal_approved_statuses), + Application.status.in_(renewal_not_fully_approved_statuses), ) ) - return query.filter(renewal_exists) + return renewal_exists class RentalProperty(Versioned, BaseModel): diff --git a/strr-api/src/strr_api/resources/registrations.py b/strr-api/src/strr_api/resources/registrations.py index 4fca4e145..b22243d9a 100644 --- a/strr-api/src/strr_api/resources/registrations.py +++ b/strr-api/src/strr_api/resources/registrations.py @@ -1038,9 +1038,9 @@ def search_registrations(): type: string description: Local government (jurisdiction) filter - in: query - name: renewalsOnly + name: reviewRenew type: boolean - description: When true, only return registrations that have a renewal application in an approved/provisional status (FULL_REVIEW_APPROVED, PROVISIONALLY_APPROVED, PROVISIONAL_REVIEW, AUTO_APPROVED). + description: When true, only return registrations that have a renewal application that is not fully approved (FULL_REVIEW, PROVISIONAL_REVIEW, PROVISIONALLY_APPROVED). responses: 200: description: @@ -1065,8 +1065,8 @@ def search_registrations(): is_set_aside_param = request.args.get("isSetAside", None) is_set_aside = is_set_aside_param.lower() == "true" if is_set_aside_param else None local_gov = request.args.get("localGov", None) or None - renewals_only_param = request.args.get("renewalsOnly", None) - renewals_only = renewals_only_param.lower() == "true" if renewals_only_param else None + review_renew_param = request.args.get("reviewRenew", None) + review_renew = review_renew_param.lower() == "true" if review_renew_param else None if sort_by not in VALID_REGISTRATION_SORT_FIELDS: sort_by = "id" if sort_order not in ["asc", "desc"]: @@ -1089,7 +1089,7 @@ def search_registrations(): noc_statuses=noc_statuses, is_set_aside=is_set_aside, local_gov=local_gov, - renewals_only=renewals_only, + review_renew=review_renew, ) registration_list = RegistrationService.search_registrations(filter_criteria=filter_criteria) diff --git a/strr-api/tests/unit/resources/test_registrations.py b/strr-api/tests/unit/resources/test_registrations.py index 67d147b19..ae0822c3e 100644 --- a/strr-api/tests/unit/resources/test_registrations.py +++ b/strr-api/tests/unit/resources/test_registrations.py @@ -2576,10 +2576,10 @@ def test_search_registrations_approval_method_uses_most_recent_application_only( @patch("strr_api.services.strr_pay.create_invoice", return_value=MOCK_INVOICE_RESPONSE) -def test_search_registrations_renewals_only_includes_registration_with_approved_renewal( +def test_search_registrations_review_renew_includes_registration_with_renewal_not_fully_approved( mock_create_invoice, session, client, jwt ): - """Test that renewalsOnly=true returns registrations that have a renewal in an approved/provisional status.""" + """Test that reviewRenew=true returns registrations that have a renewal that is not fully approved.""" from nanoid import generate with open(CREATE_HOST_REGISTRATION_REQUEST) as f: @@ -2606,23 +2606,23 @@ def test_search_registrations_renewals_only_includes_registration_with_approved_ registration_number = response_json.get("header").get("registrationNumber") registration = Registration.query.filter_by(registration_number=registration_number).one_or_none() - # Add a renewal application in an approved status + # Add a renewal application that is not fully approved (FULL_REVIEW, PROVISIONAL_REVIEW, PROVISIONALLY_APPROVED) session.refresh(application) renewal_app = Application( application_json=application.application_json, application_number=generate(alphabet="0123456789", size=14), type=ApplicationType.RENEWAL.value, registration_type=application.registration_type, - status=Application.Status.FULL_REVIEW_APPROVED.value, + status=Application.Status.FULL_REVIEW.value, registration_id=registration.id, application_date=application.application_date + timedelta(seconds=1), ) session.add(renewal_app) session.commit() - # renewalsOnly=true should return this registration (has approved renewal) + # reviewRenew=true should return this registration (has renewal that is not fully approved) rv = client.get( - f"/registrations/search?renewalsOnly=true&recordNumber={registration_number}&status={RegistrationStatus.ACTIVE.value}", + f"/registrations/search?reviewRenew=true&recordNumber={registration_number}&status={RegistrationStatus.ACTIVE.value}", headers=staff_headers, ) assert rv.status_code == HTTPStatus.OK @@ -2630,9 +2630,9 @@ def test_search_registrations_renewals_only_includes_registration_with_approved_ assert len(registrations.get("registrations")) == 1 assert registrations.get("registrations")[0].get("registrationNumber") == registration_number - # renewalsOnly=false should still return it (no filter) + # reviewRenew=false should still return it (no filter) rv = client.get( - f"/registrations/search?renewalsOnly=false&recordNumber={registration_number}", + f"/registrations/search?reviewRenew=false&recordNumber={registration_number}", headers=staff_headers, ) assert rv.status_code == HTTPStatus.OK @@ -2642,10 +2642,10 @@ def test_search_registrations_renewals_only_includes_registration_with_approved_ @patch("strr_api.services.strr_pay.create_invoice", return_value=MOCK_INVOICE_RESPONSE) -def test_search_registrations_renewals_only_excludes_registration_without_renewal( +def test_search_registrations_review_renew_excludes_registration_without_renewal( mock_create_invoice, session, client, jwt ): - """Test that renewalsOnly=true excludes registrations that have no renewal application.""" + """Test that reviewRenew=true excludes registrations that have no renewal application.""" with open(CREATE_HOST_REGISTRATION_REQUEST) as f: json_data = json.load(f) headers = create_header(jwt, [PUBLIC_USER], "Account-Id") @@ -2669,16 +2669,16 @@ def test_search_registrations_renewals_only_excludes_registration_without_renewa response_json = rv.json registration_number = response_json.get("header").get("registrationNumber") - # renewalsOnly=true should NOT return this registration (no renewal) + # reviewRenew=true should NOT return this registration (no renewal) rv = client.get( - f"/registrations/search?renewalsOnly=true&recordNumber={registration_number}&status={RegistrationStatus.ACTIVE.value}", + f"/registrations/search?reviewRenew=true&recordNumber={registration_number}&status={RegistrationStatus.ACTIVE.value}", headers=staff_headers, ) assert rv.status_code == HTTPStatus.OK registrations = rv.json assert len(registrations.get("registrations")) == 0 - # Without renewalsOnly param, registration is returned + # Without reviewRenew param, registration is returned rv = client.get( f"/registrations/search?recordNumber={registration_number}", headers=staff_headers, @@ -2690,10 +2690,8 @@ def test_search_registrations_renewals_only_excludes_registration_without_renewa @patch("strr_api.services.strr_pay.create_invoice", return_value=MOCK_INVOICE_RESPONSE) -def test_search_registrations_renewals_only_excludes_renewal_in_non_approved_status( - mock_create_invoice, session, client, jwt -): - """Test that renewalsOnly=true excludes registrations whose only renewal is not in an approved status.""" +def test_search_registrations_review_renew_excludes_renewal_fully_approved(mock_create_invoice, session, client, jwt): + """Test that reviewRenew=true excludes registrations whose only renewal is fully approved (e.g. FULL_REVIEW_APPROVED).""" from nanoid import generate with open(CREATE_HOST_REGISTRATION_REQUEST) as f: @@ -2720,23 +2718,23 @@ def test_search_registrations_renewals_only_excludes_renewal_in_non_approved_sta registration_number = response_json.get("header").get("registrationNumber") registration = Registration.query.filter_by(registration_number=registration_number).one_or_none() - # Add a renewal application in DECLINED status (not in approved/provisional set) + # Add a renewal application that is fully approved (FULL_REVIEW_APPROVED) session.refresh(application) renewal_app = Application( application_json=application.application_json, application_number=generate(alphabet="0123456789", size=14), type=ApplicationType.RENEWAL.value, registration_type=application.registration_type, - status=Application.Status.DECLINED.value, + status=Application.Status.FULL_REVIEW_APPROVED.value, registration_id=registration.id, application_date=application.application_date + timedelta(seconds=1), ) session.add(renewal_app) session.commit() - # renewalsOnly=true should NOT return this registration (renewal is DECLINED) + # reviewRenew=true should NOT return this registration (renewal is fully approved) rv = client.get( - f"/registrations/search?renewalsOnly=true&recordNumber={registration_number}&status={RegistrationStatus.ACTIVE.value}", + f"/registrations/search?reviewRenew=true&recordNumber={registration_number}&status={RegistrationStatus.ACTIVE.value}", headers=staff_headers, ) assert rv.status_code == HTTPStatus.OK diff --git a/strr-examiner-web/app/composables/useExaminerDashboardPersistence.ts b/strr-examiner-web/app/composables/useExaminerDashboardPersistence.ts index f3a9b77dc..4d60adc35 100644 --- a/strr-examiner-web/app/composables/useExaminerDashboardPersistence.ts +++ b/strr-examiner-web/app/composables/useExaminerDashboardPersistence.ts @@ -20,6 +20,7 @@ const emptyFilters = () => ({ applicantName: '', propertyAddress: '', status: [], + subStatus: [], localGov: '', adjudicator: '', submissionDate: { start: null, end: null }, @@ -55,6 +56,7 @@ function getStateFromStore (exStore: ReturnType) { applicantName: filters.applicantName, propertyAddress: filters.propertyAddress, status: [...filters.status], + subStatus: [...(filters.subStatus || [])], localGov: filters.localGov, adjudicator: filters.adjudicator, submissionDate: { start: filters.submissionDate?.start ?? null, end: filters.submissionDate?.end ?? null }, @@ -94,8 +96,10 @@ function applyStateToStore ( !isApplicationTab && (!state.filters.status || state.filters.status.length === 0) ) { - (exStore.tableFilters.status as any[]).splice( - 0, exStore.tableFilters.status.length, ...exStore.registrationsOnlyStatuses) + const statusFilters = exStore.tableFilters.status as any[] + const subStatusFilters = exStore.tableFilters.subStatus as any[] + statusFilters.splice(0, statusFilters.length, ...exStore.registrationsOnlyStatuses) + subStatusFilters.splice(0, subStatusFilters.length, ...exStore.registrationsOnlySubStatuses) } nextTick(() => { exStore.tablePage = state.page @@ -126,8 +130,8 @@ export function useExaminerDashboardPersistence ( ) { const { isSplitDashboardTableEnabled } = useExaminerFeatureFlags() // Capture "had saved state" before useSessionStorage runs, so we don't treat first visit as returning - const hadSavedAppState = hasSavedAppState() - const hadSavedRegState = hasSavedRegState() + const hadSavedAppStateOnLoad = hasSavedAppState() + const hadSavedRegStateOnLoad = hasSavedRegState() const appState = useSessionStorage(APP_KEY, defaultState()) const regState = useSessionStorage(REG_KEY, defaultState()) @@ -141,7 +145,7 @@ export function useExaminerDashboardPersistence ( if (isSplitDashboardTableEnabled.value) { const state = mergeSavedStateWithDefaults(currentState()) const isApp = isApplicationTab.value - applyStateToStore(exStore, state, isApp, isApp && !hadSavedAppState, !isApp && !hadSavedRegState) + applyStateToStore(exStore, state, isApp, isApp && !hadSavedAppStateOnLoad, !isApp && !hadSavedRegStateOnLoad) } // When the user switches tab: persist tab, save current table to storage, load the other table's state into the store @@ -179,5 +183,8 @@ export function useExaminerDashboardPersistence ( } }) - return { hasSavedAppState, hasSavedRegState } + return { + hasSavedAppState: () => hadSavedAppStateOnLoad, + hasSavedRegState: () => hadSavedRegStateOnLoad + } } diff --git a/strr-examiner-web/app/pages/dashboard.vue b/strr-examiner-web/app/pages/dashboard.vue index 24a962d4c..28f3099eb 100644 --- a/strr-examiner-web/app/pages/dashboard.vue +++ b/strr-examiner-web/app/pages/dashboard.vue @@ -267,6 +267,10 @@ const getRegistrationNocStatusDisplay = (nocStatus: RegistrationNocStatus | unde return t(`registrationNocStatus.${nocStatus}`) } +const getLatestRegistrationApplicationStatus = (reg: HousRegistrationResponse): ApplicationStatus | undefined => { + return reg.header?.applications?.[0]?.applicationStatus as ApplicationStatus | undefined +} + const getRequirementsColumn = (app: HousApplicationResponse) => { let result = '' let listingSize = '' @@ -298,6 +302,12 @@ const RENEWAL_APPROVED_STATUSES = new Set([ ApplicationStatus.AUTO_APPROVED ]) +const REVIEW_RENEW_STATUSES = new Set([ + ApplicationStatus.FULL_REVIEW, + ApplicationStatus.PROVISIONALLY_APPROVED, + ApplicationStatus.PROVISIONAL_REVIEW +]) + /** Check if a registration has been renewed. Draft renewals are ignored (Renewals in progress). */ const hasBeenRenewed = (reg: HousRegistrationResponse): boolean => { const applications = reg.header?.applications ?? [] @@ -309,6 +319,46 @@ const hasBeenRenewed = (reg: HousRegistrationResponse): boolean => { ) } +const isReviewRenew = (reg: HousRegistrationResponse): boolean => { + const applications = reg.header?.applications ?? [] + return applications.some( + app => + app.applicationType === 'renewal' && + app.applicationStatus && + REVIEW_RENEW_STATUSES.has(app.applicationStatus as ApplicationStatus) + ) +} + +const getRegistrationSubStatus = (reg: HousRegistrationResponse): string => { + if (reg.header?.isSetAside) { + return 'Set-Aside' + } + if (reg.nocStatus === RegistrationNocStatus.NOC_PENDING) { + return 'NOC - Pending' + } + if (reg.nocStatus === RegistrationNocStatus.NOC_EXPIRED) { + return 'NOC - Expired' + } + if (isReviewRenew(reg)) { + return 'Review Renew' + } + + const latestAppStatus = getLatestRegistrationApplicationStatus(reg) + if ( + latestAppStatus === ApplicationStatus.PROVISIONALLY_APPROVED || + latestAppStatus === ApplicationStatus.PROVISIONAL_REVIEW + ) { + return 'Review' + } + if ( + latestAppStatus === ApplicationStatus.AUTO_APPROVED || + latestAppStatus === ApplicationStatus.FULL_REVIEW_APPROVED + ) { + return 'Approved' + } + return '-' +} + const getConditionsColumnForRegistration = (reg: HousRegistrationResponse) => { let result = '' let listingSize = '' @@ -387,10 +437,13 @@ watch( !isApp && isEnabled && !hasSavedRegState() && - (!exStore.tableFilters.status || exStore.tableFilters.status.length === 0) + (!exStore.tableFilters.status || exStore.tableFilters.status.length === 0) && + (!exStore.tableFilters.subStatus || exStore.tableFilters.subStatus.length === 0) ) { - (exStore.tableFilters.status as any[]).splice( - 0, exStore.tableFilters.status.length, ...exStore.registrationsOnlyStatuses) + const statusFilters = exStore.tableFilters.status as any[] + const subStatusFilters = exStore.tableFilters.subStatus as any[] + statusFilters.splice(0, statusFilters.length, ...exStore.registrationsOnlyStatuses) + subStatusFilters.splice(0, subStatusFilters.length, ...exStore.registrationsOnlySubStatuses) hasAppliedRegistrationsStatusDefault.value = true } }, @@ -421,6 +474,7 @@ const { data: registrationListResp, status: regStatus } = await useAsyncData( () => exStore.tableLimit, () => exStore.tableFilters.registrationType, () => exStore.tableFilters.status, + () => exStore.tableFilters.subStatus, () => exStore.tableFilters.requirements, () => exStore.tableFilters.registrationNumber, () => exStore.tableFilters.searchText, @@ -436,6 +490,7 @@ const { data: registrationListResp, status: regStatus } = await useAsyncData( id: reg.id, registrationNumber: reg.registrationNumber, status: reg.status, + subStatus: getRegistrationSubStatus(reg), registrationType: t(`registrationType.${reg.registrationType}`), requirements: getConditionsColumnForRegistration(reg), applicantName: getApplicantNameColumnForRegistration(reg), @@ -535,6 +590,7 @@ const columns = computed(() => { sortable }, { key: 'status', label: t('page.dashboardList.columns.status'), sortable }, + ...(!isApplicationTab.value ? [{ key: 'subStatus', label: 'Sub-status', sortable }] : []), { key: 'registrationType', label: t('page.dashboardList.columns.registrationType'), sortable }, { key: 'requirements', label: t('page.dashboardList.columns.requirements'), sortable }, { key: 'applicantName', label: t('page.dashboardList.columns.hostName'), sortable }, @@ -651,19 +707,11 @@ const applicationStatusOptions: { : legacyApplicationStatusFilters const registrationStatusOptions: { label: string; value: any; disabled?: boolean }[] = [ - { label: 'Status', value: undefined, disabled: true }, + { label: 'Registration Status', value: undefined, disabled: true }, { label: 'Active', value: RegistrationStatus.ACTIVE }, { label: 'Expired', value: RegistrationStatus.EXPIRED }, { label: 'Suspended', value: RegistrationStatus.SUSPENDED }, - { label: 'Cancelled', value: RegistrationStatus.CANCELLED }, - { label: 'Approval Method', value: undefined, disabled: true }, - { label: 'Provisionally Approved', value: ApplicationStatus.PROVISIONALLY_APPROVED }, - { label: 'Fully Approved', value: ApplicationStatus.FULL_REVIEW_APPROVED }, - { label: 'Auto Approved', value: ApplicationStatus.AUTO_APPROVED }, - { label: 'Attributes', value: undefined, disabled: true }, - { label: 'NOC Expired', value: 'NOC_EXPIRED' }, - { label: 'NOC Pending', value: 'NOC_PENDING' }, - { label: 'Set Aside', value: 'SET_ASIDE' } + { label: 'Cancelled', value: RegistrationStatus.CANCELLED } ] const statusFilterOptions = computed((): { label: string; value: any; disabled?: boolean }[] => { @@ -676,6 +724,16 @@ const statusFilterOptions = computed((): { label: string; value: any; disabled?: return isApplicationTab.value ? applicationStatusOptions : registrationStatusOptions }) +const registrationSubStatusOptions: { label: string; value: any; disabled?: boolean }[] = [ + { label: 'Registration Sub-status', value: undefined, disabled: true }, + { label: 'Review', value: 'REVIEW' }, + { label: 'Review Renew', value: 'REVIEW_RENEW' }, + { label: 'NOC - Pending', value: 'NOC_PENDING' }, + { label: 'NOC - Expired', value: 'NOC_EXPIRED' }, + { label: 'Approved', value: 'APPROVED' }, + { label: 'Set-Aside', value: 'SET_ASIDE' } +] + // Update query parameter when tab changes to persist state const updateTabQuery = (isApp: boolean) => { const query = { ...route.query } @@ -942,6 +1000,18 @@ const tabLinks = computed(() => [ /> + + + + diff --git a/strr-examiner-web/app/stores/examiner.ts b/strr-examiner-web/app/stores/examiner.ts index cfa7bb02c..fb2173b48 100644 --- a/strr-examiner-web/app/stores/examiner.ts +++ b/strr-examiner-web/app/stores/examiner.ts @@ -163,8 +163,13 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { // as per requirements, registrations statuses in filter dropdown // should reflect their respective application's statuses const registrationsOnlyStatuses = [ - ApplicationStatus.PROVISIONALLY_APPROVED, - ApplicationStatus.NOC_EXPIRED + RegistrationStatus.ACTIVE + ] + + const registrationsOnlySubStatuses = [ + 'REVIEW', + 'REVIEW_RENEW', + 'NOC_PENDING' ] const tableFilters = reactive({ @@ -175,6 +180,7 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { applicantName: '', propertyAddress: '', status: [], // show all statuses + subStatus: [], submissionDate: { start: null, end: null }, lastModified: { start: null, end: null }, localGov: '', @@ -190,6 +196,9 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { // attributes of the registration const NOC_ATTR = new Set(['NOC_PENDING', 'NOC_EXPIRED']) const SET_ASIDE_ATTR = 'SET_ASIDE' + const REVIEW_SUB_STATUS = 'REVIEW' + const REVIEW_RENEW_SUB_STATUS = 'REVIEW_RENEW' + const APPROVED_SUB_STATUS = 'APPROVED' /** * Process status filters to separate application statuses, registration statuses, @@ -295,7 +304,32 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { } const fetchRegistrations = () => { - const { registrationStatuses, approvalMethods, nocStatuses, isSetAside } = processStatusFilters(tableFilters.status) + const registrationStatuses = tableFilters.status.length > 0 ? tableFilters.status : defaultRegistrationStatuses + const subStatuses = tableFilters.subStatus ?? [] + const approvalMethods = new Set() + const nocStatuses = new Set() + let isSetAside = false + let reviewRenew = false + + for (const subStatus of subStatuses) { + if (subStatus === REVIEW_SUB_STATUS) { + approvalMethods.add(ApplicationStatus.PROVISIONALLY_APPROVED) + approvalMethods.add(ApplicationStatus.PROVISIONAL_REVIEW) + } + if (subStatus === APPROVED_SUB_STATUS) { + approvalMethods.add(ApplicationStatus.AUTO_APPROVED) + approvalMethods.add(ApplicationStatus.FULL_REVIEW_APPROVED) + } + if (NOC_ATTR.has(subStatus)) { + nocStatuses.add(subStatus) + } + if (subStatus === SET_ASIDE_ATTR) { + isSetAside = true + } + if (subStatus === REVIEW_RENEW_SUB_STATUS) { + reviewRenew = true + } + } const queryParams: Record = { sortOrder: 'asc', @@ -308,15 +342,18 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { text: undefined as string | undefined } - if (approvalMethods.length > 0) { - queryParams.approvalMethod = approvalMethods + if (approvalMethods.size > 0) { + queryParams.approvalMethod = [...approvalMethods] } - if (nocStatuses.length > 0) { - queryParams.nocStatus = nocStatuses + if (nocStatuses.size > 0) { + queryParams.nocStatus = [...nocStatuses] } if (isSetAside) { queryParams.isSetAside = true } + if (reviewRenew) { + queryParams.reviewRenew = true + } if (tableFilters.searchText && tableFilters.searchText.length > 2) { queryParams.text = tableFilters.searchText @@ -638,6 +675,7 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { applicantName: '', propertyAddress: '', status: [], + subStatus: [], submissionDate: { start: null, end: null }, lastModified: { start: null, end: null }, localGov: '', @@ -656,6 +694,7 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { applicantName: '', propertyAddress: '', status: [...applicationsOnlyStatuses], + subStatus: [], submissionDate: { start: null, end: null }, lastModified: { start: null, end: null }, localGov: '', @@ -664,7 +703,7 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { tablePage.value = 1 } - /** Reset to the registrations table default state (Provisionally Approved + NOC Expired). */ + /** Reset to the registrations table default state. */ const resetFiltersToRegistrationsDefault = () => { Object.assign(tableFilters, { searchText: '', @@ -674,6 +713,7 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { applicantName: '', propertyAddress: '', status: [...registrationsOnlyStatuses], + subStatus: [...registrationsOnlySubStatuses], submissionDate: { start: null, end: null }, lastModified: { start: null, end: null }, localGov: '', @@ -744,6 +784,7 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { applicationsOnlyStatuses, registrationsOnlyStatuses, + registrationsOnlySubStatuses, viewReceipt, approveApplication, diff --git a/strr-examiner-web/package.json b/strr-examiner-web/package.json index cd9394909..d68e23d03 100644 --- a/strr-examiner-web/package.json +++ b/strr-examiner-web/package.json @@ -47,5 +47,6 @@ "nuxt": "3.15.4", "uuid": "^11.1.0", "v-calendar": "^3.1.2" - } + }, + "packageManager": "pnpm@10.21.0+sha1.a2cb35d3f07a58fecfe050af9277c5dc43a1b2e7" } diff --git a/strr-examiner-web/tests/unit/dashboard.spec.ts b/strr-examiner-web/tests/unit/dashboard.spec.ts index abfde4b96..8915badec 100644 --- a/strr-examiner-web/tests/unit/dashboard.spec.ts +++ b/strr-examiner-web/tests/unit/dashboard.spec.ts @@ -28,6 +28,7 @@ const createMockStore = (initialFilters = {}) => { applicantName: '', propertyAddress: '', status: [], + subStatus: [], submissionDate: { start: null, end: null }, lastModified: { start: null, end: null }, localGov: '', @@ -38,12 +39,16 @@ const createMockStore = (initialFilters = {}) => { const applicationsOnlyStatuses = [ ApplicationStatus.FULL_REVIEW ] + const registrationsOnlyStatuses = ['ACTIVE'] + const registrationsOnlySubStatuses = ['REVIEW', 'REVIEW_RENEW', 'NOC_PENDING'] return { tableFilters, tableLimit: ref(10), tablePage: ref(1), applicationsOnlyStatuses, + registrationsOnlyStatuses, + registrationsOnlySubStatuses, fetchApplications: vi.fn().mockResolvedValue(mockedResp), fetchRegistrations: vi.fn().mockResolvedValue({ registrations: [], total: 0 }), approveApplication: vi.fn(), @@ -61,6 +66,7 @@ const createMockStore = (initialFilters = {}) => { applicantName: '', propertyAddress: '', status: [], + subStatus: [], submissionDate: { start: null, end: null }, lastModified: { start: null, end: null }, localGov: '', diff --git a/strr-examiner-web/tests/unit/use-examiner-dashboard-persistence.spec.ts b/strr-examiner-web/tests/unit/use-examiner-dashboard-persistence.spec.ts index 120e95b47..750bc1de1 100644 --- a/strr-examiner-web/tests/unit/use-examiner-dashboard-persistence.spec.ts +++ b/strr-examiner-web/tests/unit/use-examiner-dashboard-persistence.spec.ts @@ -56,6 +56,7 @@ describe('useExaminerDashboardPersistence', () => { applicantName: '', propertyAddress: '', status: [], + subStatus: [], submissionDate: { start: null, end: null }, lastModified: { start: null, end: null }, localGov: '', @@ -63,7 +64,9 @@ describe('useExaminerDashboardPersistence', () => { }), tablePage: ref(1), tableLimit: ref(50), - applicationsOnlyStatuses: ['FULL_REVIEW'] + applicationsOnlyStatuses: ['FULL_REVIEW'], + registrationsOnlyStatuses: ['ACTIVE'], + registrationsOnlySubStatuses: ['REVIEW', 'REVIEW_RENEW', 'NOC_PENDING'] } expect(() => { @@ -83,6 +86,7 @@ describe('useExaminerDashboardPersistence', () => { applicantName: '', propertyAddress: '', status: [] as string[], + subStatus: [] as string[], submissionDate: { start: null, end: null }, lastModified: { start: null, end: null }, localGov: '', @@ -90,7 +94,9 @@ describe('useExaminerDashboardPersistence', () => { }), tablePage: ref(1), tableLimit: ref(50), - applicationsOnlyStatuses: ['FULL_REVIEW'] + applicationsOnlyStatuses: ['FULL_REVIEW'], + registrationsOnlyStatuses: ['ACTIVE'], + registrationsOnlySubStatuses: ['REVIEW', 'REVIEW_RENEW', 'NOC_PENDING'] } useExaminerDashboardPersistence(mockStore as any, ref(true)) From f5fbadd5c3b008016c8a70f01102794d2d6e504b Mon Sep 17 00:00:00 2001 From: Karim El Jazzar Date: Mon, 16 Mar 2026 13:50:58 -0700 Subject: [PATCH 2/7] updated package version after rebase --- strr-examiner-web/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/strr-examiner-web/package.json b/strr-examiner-web/package.json index d68e23d03..cd9394909 100644 --- a/strr-examiner-web/package.json +++ b/strr-examiner-web/package.json @@ -47,6 +47,5 @@ "nuxt": "3.15.4", "uuid": "^11.1.0", "v-calendar": "^3.1.2" - }, - "packageManager": "pnpm@10.21.0+sha1.a2cb35d3f07a58fecfe050af9277c5dc43a1b2e7" + } } From e6b15d20ceab79139b21e1d73fcf2de399e334fb Mon Sep 17 00:00:00 2001 From: Karim El Jazzar Date: Mon, 16 Mar 2026 13:53:09 -0700 Subject: [PATCH 3/7] removed not needed safety check --- strr-api/src/strr_api/models/rental.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/strr-api/src/strr_api/models/rental.py b/strr-api/src/strr_api/models/rental.py index e8c3bdf40..8c2b22978 100644 --- a/strr-api/src/strr_api/models/rental.py +++ b/strr-api/src/strr_api/models/rental.py @@ -443,8 +443,6 @@ def _filter_by_registration_requirement(cls, requirement: list[str], query): @classmethod def _approval_method_condition(cls, approval_methods: list[str]): """Build SQL condition for filtering by latest application status.""" - if not approval_methods: - return None # pylint: disable=import-outside-toplevel from sqlalchemy import select From 54dd7676c2bf3d84147d8dabd97bbe4b1b08a4de Mon Sep 17 00:00:00 2001 From: Karim El Jazzar Date: Mon, 16 Mar 2026 14:09:11 -0700 Subject: [PATCH 4/7] refactoring to fix SC issues --- strr-api/src/strr_api/models/rental.py | 44 ++++--- strr-examiner-web/app/pages/dashboard.vue | 9 +- strr-examiner-web/app/stores/examiner.ts | 138 ++++++++++++---------- strr-examiner-web/package.json | 3 +- 4 files changed, 114 insertions(+), 80 deletions(-) diff --git a/strr-api/src/strr_api/models/rental.py b/strr-api/src/strr_api/models/rental.py index 8c2b22978..9f954762b 100644 --- a/strr-api/src/strr_api/models/rental.py +++ b/strr-api/src/strr_api/models/rental.py @@ -118,6 +118,27 @@ class RegistrationType(BaseEnum): def search_registrations(cls, filter_criteria: RegistrationSearch): """Returns the registrations matching the search criteria.""" query = cls.query + query = cls._apply_base_search_filters(query, filter_criteria) + if filter_criteria.requirements: + query = cls._filter_by_registration_requirement(filter_criteria.requirements, query) + if filter_criteria.local_gov: + query = query.join(RentalProperty).filter( + RentalProperty.jurisdiction.ilike(f"%{filter_criteria.local_gov}%") + ) + sub_status_conditions = cls._collect_sub_status_conditions(filter_criteria) + if sub_status_conditions: + query = query.filter(db.or_(*sub_status_conditions)) + sort_column = getattr(Registration, filter_criteria.sort_by, Registration.id) + if filter_criteria.sort_order and filter_criteria.sort_order.lower() == "asc": + query = query.order_by(sort_column.asc()) + else: + query = query.order_by(sort_column.desc()) + paginated_result = query.paginate(per_page=filter_criteria.limit, page=filter_criteria.page) + return paginated_result + + @classmethod + def _apply_base_search_filters(cls, query, filter_criteria: RegistrationSearch): + """Apply common registration filters before specialized filters.""" if filter_criteria.account_id: query = query.filter(Registration.sbc_account_id == filter_criteria.account_id) if filter_criteria.search_text: @@ -139,8 +160,11 @@ def search_registrations(cls, filter_criteria: RegistrationSearch): query = query.join(User, Registration.reviewer_id == User.id).filter( User.username.ilike(f"%{filter_criteria.assignee}%") ) - if filter_criteria.requirements: - query = cls._filter_by_registration_requirement(filter_criteria.requirements, query) + return query + + @classmethod + def _collect_sub_status_conditions(cls, filter_criteria: RegistrationSearch): + """Collect OR conditions for registration sub-status filters.""" sub_status_conditions = [] if filter_criteria.approval_methods: sub_status_conditions.append(cls._approval_method_condition(filter_criteria.approval_methods)) @@ -148,23 +172,9 @@ def search_registrations(cls, filter_criteria: RegistrationSearch): sub_status_conditions.append(Registration.noc_status.in_(filter_criteria.noc_statuses)) if filter_criteria.is_set_aside is True: sub_status_conditions.append(Registration.is_set_aside == True) # noqa: E712 - if filter_criteria.local_gov: - query = query.join(RentalProperty).filter( - RentalProperty.jurisdiction.ilike(f"%{filter_criteria.local_gov}%") - ) if filter_criteria.review_renew is True: sub_status_conditions.append(cls._review_renew_condition()) - if sub_status_conditions: - # Sub-status filters are combined with OR so examiners can select - # multiple sub-status categories in a single search. - query = query.filter(db.or_(*sub_status_conditions)) - sort_column = getattr(Registration, filter_criteria.sort_by, Registration.id) - if filter_criteria.sort_order and filter_criteria.sort_order.lower() == "asc": - query = query.order_by(sort_column.asc()) - else: - query = query.order_by(sort_column.desc()) - paginated_result = query.paginate(per_page=filter_criteria.limit, page=filter_criteria.page) - return paginated_result + return sub_status_conditions @classmethod def _host_condition_bl_or_pr(cls, application_model): diff --git a/strr-examiner-web/app/pages/dashboard.vue b/strr-examiner-web/app/pages/dashboard.vue index 28f3099eb..43229102d 100644 --- a/strr-examiner-web/app/pages/dashboard.vue +++ b/strr-examiner-web/app/pages/dashboard.vue @@ -581,7 +581,7 @@ const columns = computed(() => { const sortable = enableTableFilters.value if (isSplitDashboardTableEnabled.value) { - return [ + const splitColumns = [ { key: 'registrationNumber', label: t(isApplicationTab.value @@ -590,7 +590,6 @@ const columns = computed(() => { sortable }, { key: 'status', label: t('page.dashboardList.columns.status'), sortable }, - ...(!isApplicationTab.value ? [{ key: 'subStatus', label: 'Sub-status', sortable }] : []), { key: 'registrationType', label: t('page.dashboardList.columns.registrationType'), sortable }, { key: 'requirements', label: t('page.dashboardList.columns.requirements'), sortable }, { key: 'applicantName', label: t('page.dashboardList.columns.hostName'), sortable }, @@ -598,6 +597,12 @@ const columns = computed(() => { { key: 'localGov', label: 'Local Government', sortable }, { key: 'adjudicator', label: t('page.dashboardList.columns.adjudicator'), sortable } ] + if (isApplicationTab.value) { + return splitColumns + } + // add sub-status column to registrations tab + splitColumns.splice(2, 0, { key: 'subStatus', label: 'Sub-status', sortable }) + return splitColumns } else { return [ { key: 'registrationNumber', label: t('page.dashboardList.columns.registrationNumber'), sortable }, diff --git a/strr-examiner-web/app/stores/examiner.ts b/strr-examiner-web/app/stores/examiner.ts index fb2173b48..c4e66a8a8 100644 --- a/strr-examiner-web/app/stores/examiner.ts +++ b/strr-examiner-web/app/stores/examiner.ts @@ -260,52 +260,14 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { return { applicationStatuses, registrationStatuses, approvalMethods, nocStatuses, isSetAside } } - const fetchApplications = () => { - const { applicationStatuses, registrationStatuses } = processStatusFilters(tableFilters.status) - const applicationsOnly = isSplitDashboardTableEnabled.value - if (tableFilters.searchText && tableFilters.searchText.length > 2) { - return $strrApi('/applications/search', { - query: { - limit: tableLimit.value, - page: tablePage.value, - registrationType: tableFilters.registrationType, - status: applicationStatuses, - registrationStatus: registrationStatuses, - text: tableFilters.searchText, - sortBy: ApplicationSortBy.APPLICATION_DATE, - sortOrder: ApplicationSortOrder.ASC, - address: tableFilters.propertyAddress, - recordNumber: tableFilters.registrationNumber, - assignee: tableFilters.adjudicator, - requirement: tableFilters.requirements, - localGov: tableFilters.localGov || undefined - } - }) - } else { - return $strrApi('/applications', { - query: { - limit: tableLimit.value, - page: tablePage.value, - registrationType: tableFilters.registrationType, - status: applicationStatuses, - registrationStatus: registrationStatuses, - sortBy: ApplicationSortBy.APPLICATION_DATE, - sortOrder: ApplicationSortOrder.ASC, - address: tableFilters.propertyAddress, - recordNumber: tableFilters.registrationNumber, - assignee: tableFilters.adjudicator, - requirement: tableFilters.requirements, - localGov: tableFilters.localGov || undefined, - includeDraftRegistration: false, - applicationsOnly - } - }) - } + type RegistrationSubStatusFilters = { + approvalMethods: string[] + nocStatuses: string[] + isSetAside: boolean + reviewRenew: boolean } - const fetchRegistrations = () => { - const registrationStatuses = tableFilters.status.length > 0 ? tableFilters.status : defaultRegistrationStatuses - const subStatuses = tableFilters.subStatus ?? [] + const mapRegistrationSubStatuses = (subStatuses: string[]): RegistrationSubStatusFilters => { const approvalMethods = new Set() const nocStatuses = new Set() let isSetAside = false @@ -315,22 +277,30 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { if (subStatus === REVIEW_SUB_STATUS) { approvalMethods.add(ApplicationStatus.PROVISIONALLY_APPROVED) approvalMethods.add(ApplicationStatus.PROVISIONAL_REVIEW) - } - if (subStatus === APPROVED_SUB_STATUS) { + } else if (subStatus === APPROVED_SUB_STATUS) { approvalMethods.add(ApplicationStatus.AUTO_APPROVED) approvalMethods.add(ApplicationStatus.FULL_REVIEW_APPROVED) - } - if (NOC_ATTR.has(subStatus)) { + } else if (NOC_ATTR.has(subStatus)) { nocStatuses.add(subStatus) - } - if (subStatus === SET_ASIDE_ATTR) { + } else if (subStatus === SET_ASIDE_ATTR) { isSetAside = true - } - if (subStatus === REVIEW_RENEW_SUB_STATUS) { + } else if (subStatus === REVIEW_RENEW_SUB_STATUS) { reviewRenew = true } } + return { + approvalMethods: [...approvalMethods], + nocStatuses: [...nocStatuses], + isSetAside, + reviewRenew + } + } + + const buildRegistrationQueryParams = ( + registrationStatuses: string[], + mappedSubStatuses: RegistrationSubStatusFilters + ): Record => { const queryParams: Record = { sortOrder: 'asc', limit: tableLimit.value, @@ -342,30 +312,78 @@ export const useExaminerStore = defineStore('strr/examiner-store', () => { text: undefined as string | undefined } - if (approvalMethods.size > 0) { - queryParams.approvalMethod = [...approvalMethods] + if (mappedSubStatuses.approvalMethods.length > 0) { + queryParams.approvalMethod = mappedSubStatuses.approvalMethods } - if (nocStatuses.size > 0) { - queryParams.nocStatus = [...nocStatuses] + if (mappedSubStatuses.nocStatuses.length > 0) { + queryParams.nocStatus = mappedSubStatuses.nocStatuses } - if (isSetAside) { + if (mappedSubStatuses.isSetAside) { queryParams.isSetAside = true } - if (reviewRenew) { + if (mappedSubStatuses.reviewRenew) { queryParams.reviewRenew = true } if (tableFilters.searchText && tableFilters.searchText.length > 2) { queryParams.text = tableFilters.searchText } - if (tableFilters.localGov) { queryParams.localGov = tableFilters.localGov } - if (tableFilters.adjudicator) { queryParams.assignee = tableFilters.adjudicator } + return queryParams + } + + const fetchApplications = () => { + const { applicationStatuses, registrationStatuses } = processStatusFilters(tableFilters.status) + const applicationsOnly = isSplitDashboardTableEnabled.value + if (tableFilters.searchText && tableFilters.searchText.length > 2) { + return $strrApi('/applications/search', { + query: { + limit: tableLimit.value, + page: tablePage.value, + registrationType: tableFilters.registrationType, + status: applicationStatuses, + registrationStatus: registrationStatuses, + text: tableFilters.searchText, + sortBy: ApplicationSortBy.APPLICATION_DATE, + sortOrder: ApplicationSortOrder.ASC, + address: tableFilters.propertyAddress, + recordNumber: tableFilters.registrationNumber, + assignee: tableFilters.adjudicator, + requirement: tableFilters.requirements, + localGov: tableFilters.localGov || undefined + } + }) + } else { + return $strrApi('/applications', { + query: { + limit: tableLimit.value, + page: tablePage.value, + registrationType: tableFilters.registrationType, + status: applicationStatuses, + registrationStatus: registrationStatuses, + sortBy: ApplicationSortBy.APPLICATION_DATE, + sortOrder: ApplicationSortOrder.ASC, + address: tableFilters.propertyAddress, + recordNumber: tableFilters.registrationNumber, + assignee: tableFilters.adjudicator, + requirement: tableFilters.requirements, + localGov: tableFilters.localGov || undefined, + includeDraftRegistration: false, + applicationsOnly + } + }) + } + } + + const fetchRegistrations = () => { + const registrationStatuses = tableFilters.status.length > 0 ? tableFilters.status : defaultRegistrationStatuses + const mappedSubStatuses = mapRegistrationSubStatuses(tableFilters.subStatus ?? []) + const queryParams = buildRegistrationQueryParams(registrationStatuses, mappedSubStatuses) return $strrApi('/registrations/search', { query: queryParams diff --git a/strr-examiner-web/package.json b/strr-examiner-web/package.json index cd9394909..d68e23d03 100644 --- a/strr-examiner-web/package.json +++ b/strr-examiner-web/package.json @@ -47,5 +47,6 @@ "nuxt": "3.15.4", "uuid": "^11.1.0", "v-calendar": "^3.1.2" - } + }, + "packageManager": "pnpm@10.21.0+sha1.a2cb35d3f07a58fecfe050af9277c5dc43a1b2e7" } From bb2c4238bfe7aee900aa31a4491462564dc72dc5 Mon Sep 17 00:00:00 2001 From: Karim El Jazzar Date: Mon, 16 Mar 2026 14:25:31 -0700 Subject: [PATCH 5/7] removed package manager --- strr-examiner-web/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/strr-examiner-web/package.json b/strr-examiner-web/package.json index d68e23d03..cd9394909 100644 --- a/strr-examiner-web/package.json +++ b/strr-examiner-web/package.json @@ -47,6 +47,5 @@ "nuxt": "3.15.4", "uuid": "^11.1.0", "v-calendar": "^3.1.2" - }, - "packageManager": "pnpm@10.21.0+sha1.a2cb35d3f07a58fecfe050af9277c5dc43a1b2e7" + } } From 76c0430d5495c9273965d01fa548e73071b26baf Mon Sep 17 00:00:00 2001 From: Karim El Jazzar Date: Tue, 17 Mar 2026 09:04:32 -0700 Subject: [PATCH 6/7] updated package version after rebase --- strr-examiner-web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strr-examiner-web/package.json b/strr-examiner-web/package.json index cd9394909..d74cd3958 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.2.25", + "version": "0.2.26", "scripts": { "build-check": "nuxt build", "build": "nuxt generate", From a50dca3eb814cf60200d24edc4d13d04187b3c4d Mon Sep 17 00:00:00 2001 From: Karim El Jazzar Date: Tue, 17 Mar 2026 09:11:10 -0700 Subject: [PATCH 7/7] fixed test thats failing after rebase --- strr-examiner-web/tests/unit/store-examiner.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/strr-examiner-web/tests/unit/store-examiner.spec.ts b/strr-examiner-web/tests/unit/store-examiner.spec.ts index de376a525..237ce4684 100644 --- a/strr-examiner-web/tests/unit/store-examiner.spec.ts +++ b/strr-examiner-web/tests/unit/store-examiner.spec.ts @@ -144,6 +144,7 @@ describe('Store - Examiner', () => { applicantName: '', propertyAddress: '', status: [], + subStatus: [], submissionDate: { start: null, end: null }, lastModified: { start: null, end: null }, localGov: '',