Skip to content

Commit f6f876d

Browse files
ronaldo-macapobreRonaldo Macapobre
andauthored
JASPER-738: Digital Signing: Add the judge selector to the Order processing queue screen (#838)
* judge selector in orders screen wip * Add logic to show/hide order review options * - Move app-bar code to its own component to keep App.vue small and simple - Add unit tests for AppBar - Fix failing unit tests * Fix sonar cloud issue * Refactor Orders and AppBar components to improve state management and initialization logic --------- Co-authored-by: Ronaldo Macapobre <rmacapob@gov.bc.ca>
1 parent 022f217 commit f6f876d

File tree

9 files changed

+1024
-310
lines changed

9 files changed

+1024
-310
lines changed

web/src/App.vue

Lines changed: 5 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,143 +1,30 @@
11
<template>
22
<v-theme-provider :theme="theme">
33
<v-app>
4-
<profile-off-canvas v-model="profile" />
5-
<v-app-bar app>
6-
<v-app-bar-title class="mr-4">
7-
<router-link to="/">
8-
<img class="logo" :src="logo" alt="logo" width="63" />
9-
</router-link>
10-
</v-app-bar-title>
11-
<v-tabs align-tabs="start" v-model="selectedTab">
12-
<v-tab value="dashboard" to="/dashboard">Dashboard</v-tab>
13-
<v-tab value="court-list" to="/court-list">Court list</v-tab>
14-
<v-tab value="court-file-search" to="/court-file-search"
15-
>Court file search</v-tab
16-
>
17-
<v-btn
18-
class="v-tab underline-on-hover"
19-
value="dars"
20-
@click="darsStore.openModal()"
21-
>DARS</v-btn
22-
>
23-
<v-tab value="orders" to="/orders" v-if="showOrders">
24-
<v-badge
25-
v-if="pendingOrdersCount > 0"
26-
:content="pendingOrdersCount"
27-
color="error"
28-
offset-x="-10"
29-
offset-y="-10"
30-
>
31-
For Signing
32-
</v-badge>
33-
<template v-else>For Signing</template>
34-
</v-tab>
35-
<v-spacer></v-spacer>
36-
<div class="d-flex align-center">
37-
<JudgeSelector
38-
v-if="
39-
(selectedTab === 'dashboard' || selectedTab === 'court-list') &&
40-
judges &&
41-
judges?.length > 0
42-
"
43-
:judges="judges"
44-
/>
45-
<v-btn
46-
spaced="end"
47-
size="x-large"
48-
@click.stop="profile = true"
49-
class="text-subtitle-1"
50-
>
51-
<span class="text-left">
52-
<div class="mb-1">{{ userName }}</div>
53-
</span>
54-
<template #append>
55-
<v-icon :icon="mdiAccountCircle" size="32" />
56-
</template>
57-
</v-btn>
58-
</div>
59-
</v-tabs>
60-
</v-app-bar>
4+
<ProfileOffCanvas v-model="profile" />
5+
<AppBar @open-profile="profile = true" />
616
<v-main>
627
<router-view />
638
</v-main>
649
<DarsAccessModal v-model="darsStore.isModalVisible" />
65-
<snackbar />
10+
<Snackbar />
6611
</v-app>
6712
</v-theme-provider>
6813
</template>
6914

7015
<script setup lang="ts">
71-
import logo from '@/assets/jasper-logo.svg?url';
72-
import { JudgeService, OrderService } from '@/services';
73-
import { useCommonStore } from '@/stores';
74-
import { PersonSearchItem } from '@/types';
75-
import { OrderReviewStatus, RolesEnum } from '@/types/common';
76-
import { mdiAccountCircle } from '@mdi/js';
77-
import { computed, inject, onMounted, ref, watch } from 'vue';
78-
import { useRoute } from 'vue-router';
16+
import { ref } from 'vue';
7917
import DarsAccessModal from './components/dashboard/DarsAccessModal.vue';
80-
import JudgeSelector from './components/shared/JudgeSelector.vue';
18+
import AppBar from './components/shared/AppBar.vue';
8119
import ProfileOffCanvas from './components/shared/ProfileOffCanvas.vue';
8220
import Snackbar from './components/shared/Snackbar.vue';
8321
import { useDarsStore } from './stores/DarsStore';
84-
import { useOrdersStore } from './stores/OrdersStore';
8522
import { useThemeStore } from './stores/ThemeStore';
8623
8724
const themeStore = useThemeStore();
88-
const commonStore = useCommonStore();
8925
const darsStore = useDarsStore();
90-
const ordersStore = useOrdersStore();
9126
const theme = ref(themeStore.state);
9227
const profile = ref(false);
93-
94-
const route = useRoute();
95-
const selectedTab = ref('/dashboard');
96-
const orderService = inject<OrderService>('orderService');
97-
const judgeService = inject<JudgeService>('judgeService');
98-
const judges = ref<PersonSearchItem[]>([]);
99-
100-
if (!judgeService || !orderService) {
101-
throw new Error('Service is not available!');
102-
}
103-
104-
onMounted(async () => {
105-
const [judgesData] = await Promise.all([
106-
judgeService?.getJudges(),
107-
ordersStore.fetchOrders(orderService),
108-
]);
109-
judges.value = judgesData ?? [];
110-
});
111-
112-
watch(
113-
() => route.path,
114-
(newPath) => {
115-
if (
116-
newPath.startsWith('/civil-file') ||
117-
newPath.startsWith('/criminal-file')
118-
) {
119-
selectedTab.value = 'court-file-search';
120-
} else {
121-
selectedTab.value = newPath;
122-
}
123-
}
124-
);
125-
126-
const userName = computed(() => commonStore.userInfo?.userTitle || '');
127-
// Only users with Admin role can see Orders tab for now.
128-
const requiredOrderRoles = [RolesEnum.Admin] as const;
129-
const showOrders = computed(
130-
() =>
131-
requiredOrderRoles.every((requiredRole) =>
132-
commonStore.userInfo?.roles?.includes(requiredRole)
133-
) ?? false
134-
);
135-
136-
const pendingOrdersCount = computed(
137-
() =>
138-
ordersStore.orders.filter((o) => o.status === OrderReviewStatus.Pending)
139-
.length
140-
);
14128
</script>
14229

14330
<style>
@@ -148,22 +35,4 @@
14835
.v-toolbar-title {
14936
flex: none;
15037
}
151-
152-
.logo {
153-
transition:
154-
transform 0.3s ease,
155-
filter 0.3s ease;
156-
}
157-
158-
.logo:hover {
159-
transform: scale(1.02);
160-
filter: brightness(1.1);
161-
}
162-
</style>
163-
164-
<style scoped>
165-
.underline-on-hover:hover :deep(.v-btn__content) {
166-
text-decoration: underline;
167-
text-underline-offset: 2px;
168-
}
16938
</style>

web/src/components/documents/strategies/OrderPDFStrategy.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
import { OrderReview } from '@/types/OrderReview';
2-
import { BasePDFStrategy } from './BasePDFStrategy';
31
import { OrderService } from '@/services/OrderService';
4-
import { inject } from 'vue';
2+
import { useCommonStore, useSnackbarStore } from '@/stores';
53
import { OrderReviewStatus } from '@/types/common';
6-
import { useSnackbarStore } from '@/stores/SnackbarStore';
4+
import { OrderReview } from '@/types/OrderReview';
5+
import { inject } from 'vue';
6+
import { BasePDFStrategy } from './BasePDFStrategy';
77

88
export class OrderPDFStrategy extends BasePDFStrategy {
99
showOrderReviewOptions = true;
1010
defaultDocumentName = 'Order';
1111
private readonly snackBarStore = useSnackbarStore();
12+
private readonly commonStore = useCommonStore();
1213
private readonly orderService: OrderService;
1314

1415
constructor() {
@@ -18,6 +19,11 @@ export class OrderPDFStrategy extends BasePDFStrategy {
1819
throw new Error('Service(s) is undefined.');
1920
}
2021
this.orderService = orderService;
22+
23+
// Only show review options if the logged-in user is viewing their own data
24+
this.showOrderReviewOptions =
25+
this.commonStore.userInfo?.judgeId ===
26+
this.commonStore.loggedInUserInfo?.judgeId;
2127
}
2228

2329
async reviewOrder(review: OrderReview): Promise<void> {

web/src/components/orders/Orders.vue

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,34 @@
6060
</template>
6161
<script lang="ts" setup>
6262
import shared from '@/components/shared';
63-
import { useCourtFileSearchStore, useOrdersStore } from '@/stores';
63+
import { OrderService } from '@/services';
64+
import {
65+
useCommonStore,
66+
useCourtFileSearchStore,
67+
useOrdersStore,
68+
} from '@/stores';
6469
import { Order } from '@/types';
6570
import { KeyValueInfo, OrderReviewStatus } from '@/types/common';
6671
import { DocumentData } from '@/types/shared';
6772
import { getCourtClassLabel, isCourtClassLabelCriminal } from '@/utils/utils';
68-
import { computed } from 'vue';
73+
import { computed, inject, onMounted } from 'vue';
6974
7075
const ordersStore = useOrdersStore();
7176
const courtFileSearchStore = useCourtFileSearchStore();
77+
const commonStore = useCommonStore();
78+
const orderService = inject<OrderService>('orderService');
79+
80+
if (!orderService) {
81+
throw new Error('Service is not available!');
82+
}
83+
84+
// Create a reactive reference to judgeId for the orders store
85+
const judgeId = computed(() => commonStore.userInfo?.judgeId ?? null);
86+
87+
onMounted(async () => {
88+
// Initialize orders store with reactive judgeId source
89+
ordersStore.initialize(orderService, judgeId);
90+
});
7291
7392
const pendingOrders = computed(
7493
() =>

0 commit comments

Comments
 (0)