|
1 | 1 | <template> |
2 | 2 | <v-theme-provider :theme="theme"> |
3 | 3 | <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" /> |
61 | 6 | <v-main> |
62 | 7 | <router-view /> |
63 | 8 | </v-main> |
64 | 9 | <DarsAccessModal v-model="darsStore.isModalVisible" /> |
65 | | - <snackbar /> |
| 10 | + <Snackbar /> |
66 | 11 | </v-app> |
67 | 12 | </v-theme-provider> |
68 | 13 | </template> |
69 | 14 |
|
70 | 15 | <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'; |
79 | 17 | import DarsAccessModal from './components/dashboard/DarsAccessModal.vue'; |
80 | | - import JudgeSelector from './components/shared/JudgeSelector.vue'; |
| 18 | + import AppBar from './components/shared/AppBar.vue'; |
81 | 19 | import ProfileOffCanvas from './components/shared/ProfileOffCanvas.vue'; |
82 | 20 | import Snackbar from './components/shared/Snackbar.vue'; |
83 | 21 | import { useDarsStore } from './stores/DarsStore'; |
84 | | - import { useOrdersStore } from './stores/OrdersStore'; |
85 | 22 | import { useThemeStore } from './stores/ThemeStore'; |
86 | 23 |
|
87 | 24 | const themeStore = useThemeStore(); |
88 | | - const commonStore = useCommonStore(); |
89 | 25 | const darsStore = useDarsStore(); |
90 | | - const ordersStore = useOrdersStore(); |
91 | 26 | const theme = ref(themeStore.state); |
92 | 27 | 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 | | - ); |
141 | 28 | </script> |
142 | 29 |
|
143 | 30 | <style> |
|
148 | 35 | .v-toolbar-title { |
149 | 36 | flex: none; |
150 | 37 | } |
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 | | - } |
169 | 38 | </style> |
0 commit comments