@7365admin1/layer-common 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.editorconfig +12 -0
  4. package/.github/workflows/main.yml +17 -0
  5. package/.github/workflows/publish.yml +39 -0
  6. package/.nuxtrc +1 -0
  7. package/.playground/app.vue +41 -0
  8. package/.playground/eslint.config.mjs +6 -0
  9. package/.playground/nuxt.config.ts +22 -0
  10. package/.playground/pages/feedback.vue +30 -0
  11. package/CHANGELOG.md +263 -0
  12. package/README.md +73 -0
  13. package/app.vue +3 -0
  14. package/components/AccessCardAddForm.vue +363 -0
  15. package/components/AccessManagement.vue +420 -0
  16. package/components/Avatar/Main.vue +68 -0
  17. package/components/BillingMain.vue +66 -0
  18. package/components/BtnUploadFile.vue +139 -0
  19. package/components/BuildingForm.vue +303 -0
  20. package/components/BuildingManagement/buildings.vue +335 -0
  21. package/components/BuildingManagement/units.vue +350 -0
  22. package/components/BuildingUnitFormAdd.vue +441 -0
  23. package/components/BuildingUnitFormEdit.vue +429 -0
  24. package/components/CameraForm.vue +264 -0
  25. package/components/CameraMain.vue +352 -0
  26. package/components/Card/DeleteConfirmation.vue +51 -0
  27. package/components/Card/MemberInfoSummary.vue +44 -0
  28. package/components/Card/Toggle.vue +25 -0
  29. package/components/Chat/Bubbles.vue +53 -0
  30. package/components/Chat/Information.vue +416 -0
  31. package/components/Chat/ListCard.vue +62 -0
  32. package/components/Chat/Message.vue +158 -0
  33. package/components/Chat/Navigation.vue +150 -0
  34. package/components/ConfirmDialog.vue +66 -0
  35. package/components/Container/Standard.vue +33 -0
  36. package/components/DashboardPlaceholder.vue +1524 -0
  37. package/components/Dialog/DeleteConfirmation.vue +51 -0
  38. package/components/Dialog/ReplaceAutofillPrompt.vue +49 -0
  39. package/components/Dialog/UpdateMoreAction.vue +103 -0
  40. package/components/DocumentForm.vue +187 -0
  41. package/components/DocumentManagement.vue +376 -0
  42. package/components/Editor.vue +95 -0
  43. package/components/EntryPassMain.vue +518 -0
  44. package/components/Feedback/Form.vue +173 -0
  45. package/components/FeedbackDetail.vue +599 -0
  46. package/components/FeedbackMain.vue +588 -0
  47. package/components/FormDialog.vue +65 -0
  48. package/components/ImageCarousel.vue +138 -0
  49. package/components/Input/Date.vue +177 -0
  50. package/components/Input/DateTimePicker.vue +131 -0
  51. package/components/Input/File.vue +236 -0
  52. package/components/Input/FileV2.vue +234 -0
  53. package/components/Input/InputPhoneNumberV2.vue +164 -0
  54. package/components/Input/ListGroupSelection.vue +96 -0
  55. package/components/Input/NRICNumber.vue +53 -0
  56. package/components/Input/NewDate.vue +123 -0
  57. package/components/Input/Number.vue +124 -0
  58. package/components/Input/Password.vue +22 -0
  59. package/components/Input/PhoneNumber.vue +188 -0
  60. package/components/Input/VehicleNumber.vue +49 -0
  61. package/components/InputLabel.vue +22 -0
  62. package/components/InvitationForm.vue +359 -0
  63. package/components/InvitationMain.vue +310 -0
  64. package/components/Layout/Header.vue +129 -0
  65. package/components/Layout/NavigationDrawer.vue +44 -0
  66. package/components/ListItem.vue +35 -0
  67. package/components/ListView.vue +87 -0
  68. package/components/LocalPagination.vue +31 -0
  69. package/components/MemberMain.vue +459 -0
  70. package/components/NFC/NFCPatrolReportMain.vue +591 -0
  71. package/components/NFC/NFCPatrolRouteForm.vue +596 -0
  72. package/components/NFC/NFCPatrolRouteMain.vue +539 -0
  73. package/components/NFC/NFCTagForm.vue +236 -0
  74. package/components/NFC/NFCTagMain.vue +337 -0
  75. package/components/NFC/PatrolSettings.vue +130 -0
  76. package/components/NavigationItem.vue +83 -0
  77. package/components/NumberSettingField.vue +107 -0
  78. package/components/OnlineFormConfigurationForm.vue +290 -0
  79. package/components/OnlineFormsConfiguration.vue +429 -0
  80. package/components/PeopleForm.vue +452 -0
  81. package/components/PlaceholderComponent.vue +34 -0
  82. package/components/RolePermissionFormCreate.vue +161 -0
  83. package/components/RolePermissionFormPreviewUpdate.vue +183 -0
  84. package/components/RolePermissionMain.vue +361 -0
  85. package/components/SearchVehicleNumberUser.vue +91 -0
  86. package/components/ServiceProviderFormCreate.vue +154 -0
  87. package/components/ServiceProviderMain.vue +547 -0
  88. package/components/SignaturePad.vue +73 -0
  89. package/components/Snackbar.vue +23 -0
  90. package/components/SpecificAttr.vue +53 -0
  91. package/components/SupplyManagement.vue +292 -0
  92. package/components/SwitchContext.vue +108 -0
  93. package/components/TableList.vue +150 -0
  94. package/components/TableListSecondary.vue +164 -0
  95. package/components/TableMain.vue +142 -0
  96. package/components/TableWithButton.vue +94 -0
  97. package/components/VehicleUpdateMoreAction.vue +84 -0
  98. package/components/VideoPlayer.vue +125 -0
  99. package/components/VisitorForm.vue +659 -0
  100. package/components/VisitorFormSelection.vue +53 -0
  101. package/components/VisitorManagement.vue +490 -0
  102. package/components/WorkOrder/Create.vue +284 -0
  103. package/components/WorkOrder/Detail.vue +71 -0
  104. package/components/WorkOrder/ListView.vue +96 -0
  105. package/components/WorkOrder/Main.vue +489 -0
  106. package/components/Workorder.vue +1 -0
  107. package/composables/useAddress.ts +107 -0
  108. package/composables/useBuilding.ts +250 -0
  109. package/composables/useBuildingUnit.ts +116 -0
  110. package/composables/useCard.ts +46 -0
  111. package/composables/useCommonPermission.ts +207 -0
  112. package/composables/useCustomer.ts +113 -0
  113. package/composables/useCustomerSite.ts +56 -0
  114. package/composables/useDashboard.ts +31 -0
  115. package/composables/useDashboardData.ts +425 -0
  116. package/composables/useDocument.ts +57 -0
  117. package/composables/useFacility.ts +246 -0
  118. package/composables/useFeedback.ts +119 -0
  119. package/composables/useFile.ts +55 -0
  120. package/composables/useInvoice.ts +18 -0
  121. package/composables/useLocal.ts +131 -0
  122. package/composables/useLocalAuth.ts +137 -0
  123. package/composables/useLocalSetup.ts +13 -0
  124. package/composables/useMember.ts +111 -0
  125. package/composables/useNFCPatrolRoute.ts +77 -0
  126. package/composables/useNFCPatrolSettings.ts +19 -0
  127. package/composables/useNFCPatrolTag.ts +53 -0
  128. package/composables/useOnlineForm.ts +67 -0
  129. package/composables/useOrg.ts +129 -0
  130. package/composables/usePDFDownload.ts +25 -0
  131. package/composables/usePaymentMethod.ts +101 -0
  132. package/composables/usePeople.ts +81 -0
  133. package/composables/usePermission.ts +54 -0
  134. package/composables/usePhoneCountries.ts +561 -0
  135. package/composables/usePrice.ts +15 -0
  136. package/composables/usePromoCode.ts +36 -0
  137. package/composables/useRecapPermission.ts +26 -0
  138. package/composables/useRole.ts +104 -0
  139. package/composables/useSecurityUtils.ts +18 -0
  140. package/composables/useServiceProvider.ts +224 -0
  141. package/composables/useSite.ts +109 -0
  142. package/composables/useSiteEntryPassSettings.ts +46 -0
  143. package/composables/useSiteSettings.ts +123 -0
  144. package/composables/useSubscription.ts +150 -0
  145. package/composables/useUser.ts +132 -0
  146. package/composables/useUtils.ts +445 -0
  147. package/composables/useVerification.ts +34 -0
  148. package/composables/useVisitor.ts +120 -0
  149. package/composables/useWorkOrder.ts +85 -0
  150. package/error.vue +41 -0
  151. package/layouts/plain.vue +7 -0
  152. package/middleware/01.auth.ts +20 -0
  153. package/middleware/02.org.ts +21 -0
  154. package/middleware/03.customer.ts +13 -0
  155. package/middleware/member.ts +4 -0
  156. package/nuxt.config.ts +54 -0
  157. package/package.json +39 -0
  158. package/pages/index.vue +3 -0
  159. package/pages/payment-method-linked.vue +31 -0
  160. package/pages/require-customer.vue +56 -0
  161. package/pages/require-organization-membership.vue +47 -0
  162. package/pages/unauthorized.vue +29 -0
  163. package/plugins/API.ts +21 -0
  164. package/plugins/iconify.client.ts +5 -0
  165. package/plugins/secure-member.client.ts +86 -0
  166. package/plugins/vuetify.ts +62 -0
  167. package/public/bg-camera.jpg +0 -0
  168. package/public/bg-city.jpg +0 -0
  169. package/public/bg-condo.jpg +0 -0
  170. package/public/images/icons/delete-icon.png +0 -0
  171. package/public/sprite.svg +1 -0
  172. package/tsconfig.json +3 -0
  173. package/types/address.d.ts +13 -0
  174. package/types/building.d.ts +27 -0
  175. package/types/camera.d.ts +31 -0
  176. package/types/card.d.ts +22 -0
  177. package/types/customer.d.ts +27 -0
  178. package/types/document.d.ts +6 -0
  179. package/types/feedback.d.ts +68 -0
  180. package/types/local.d.ts +74 -0
  181. package/types/member.d.ts +21 -0
  182. package/types/online-form.d.ts +15 -0
  183. package/types/org.d.ts +13 -0
  184. package/types/people.d.ts +24 -0
  185. package/types/permission.d.ts +25 -0
  186. package/types/phone-number.d.ts +10 -0
  187. package/types/price.d.ts +17 -0
  188. package/types/promo-code.d.ts +19 -0
  189. package/types/role.d.ts +11 -0
  190. package/types/select.d.ts +4 -0
  191. package/types/service-provider.d.ts +15 -0
  192. package/types/site.d.ts +20 -0
  193. package/types/subscription.d.ts +23 -0
  194. package/types/user.d.ts +19 -0
  195. package/types/verification.d.ts +20 -0
  196. package/types/visitor.d.ts +42 -0
  197. package/types/work-order.d.ts +42 -0
  198. package/utils/phoneMasks.ts +1703 -0
@@ -0,0 +1,416 @@
1
+ <template>
2
+ <v-sheet
3
+ elevation="1"
4
+ rounded="0"
5
+ class="fill-height d-flex flex-column"
6
+ style="border-right: 1px solid #e0e0e0; border-top: 1px solid #e0e0e0"
7
+ >
8
+ <v-row class="pa-4">
9
+ <v-sheet
10
+ class="overflow-y-auto flex-grow-1"
11
+ :style="{ height: 'calc(100vh - 250px)', overflowX: 'hidden' }"
12
+ >
13
+ <v-col
14
+ cols="12"
15
+ class="mb-2"
16
+ style="position: sticky; top: 0; background-color: white; z-index: 1"
17
+ >
18
+ <h3 class="text-h6">
19
+ {{ type === "workOrder" ? "Work Order" : "Feedback" }} Information
20
+ </h3>
21
+ </v-col>
22
+
23
+ <v-col cols="12" class="text-center mb-0" v-if="item">
24
+ <v-img
25
+ v-if="item.attachments.length === 1"
26
+ :src="`/api/public/${item.attachments[0]}`"
27
+ height="200"
28
+ cover
29
+ class="rounded mb-2"
30
+ />
31
+ <v-carousel
32
+ v-else-if="item.attachments.length > 1"
33
+ hide-delimiter-background
34
+ height="200"
35
+ class="rounded mb-2"
36
+ show-arrows
37
+ cycle
38
+ >
39
+ <v-carousel-item
40
+ v-for="(attachment, index) in item.attachments"
41
+ :key="index"
42
+ >
43
+ <v-img
44
+ :src="`/api/public/${attachment}`"
45
+ cover
46
+ class="w-100 h-100"
47
+ />
48
+ </v-carousel-item>
49
+ </v-carousel>
50
+ </v-col>
51
+
52
+ <v-col cols="12" v-if="item">
53
+ <v-row dense class="my-1" v-if="type === 'feedback'">
54
+ <v-col cols="12" class="py-1"><strong>Subject:</strong></v-col>
55
+ <v-col cols="12" class="py-1">
56
+ {{ item.subject }}
57
+ </v-col>
58
+ </v-row>
59
+
60
+ <v-divider />
61
+
62
+ <v-row dense class="my-1 pr-4">
63
+ <v-col cols="6" class="py-1"><strong>Category:</strong></v-col>
64
+ <v-col cols="6" class="py-1 text-right text-capitalize">{{
65
+ item.category
66
+ }}</v-col>
67
+ </v-row>
68
+
69
+ <v-divider />
70
+
71
+ <v-row dense class="my-1">
72
+ <v-col cols="6" class="py-1"><strong>Location:</strong></v-col>
73
+ <v-col cols="6" class="py-1 text-right">{{ item.location }}</v-col>
74
+ </v-row>
75
+ <v-divider />
76
+
77
+ <v-row dense class="my-1" v-if="type === 'feedback'">
78
+ <v-col cols="12" class="py-1"
79
+ ><strong>Description Feedback:</strong></v-col
80
+ >
81
+ <v-col cols="12" class="py-1">
82
+ {{ item.description }}
83
+ </v-col>
84
+ </v-row>
85
+ <v-divider />
86
+
87
+ <v-row dense class="my-1" v-if="type === 'workOrder'">
88
+ <v-col cols="12" class="py-1"
89
+ ><strong>Description Work Order:</strong></v-col
90
+ >
91
+ <v-col cols="12" class="py-1">
92
+ {{ item.description || "No work order description available" }}
93
+ </v-col>
94
+ </v-row>
95
+ <v-divider />
96
+
97
+ <v-row dense class="my-1" v-if="type === 'feedback'">
98
+ <v-col cols="6" class="py-1"
99
+ ><strong>Work Order No.:</strong></v-col
100
+ >
101
+ <v-col v-if="item.workOrderNo" cols="6" class="py-1 text-right">
102
+ <NuxtLink :to="showWorkOrderInfo(item.workOrderId)">{{
103
+ item.workOrderNo
104
+ }}</NuxtLink>
105
+ </v-col>
106
+ <v-col v-else cols="6" class="py-1 text-right">
107
+ <v-btn
108
+ class="text-none text-capitalize text-blue"
109
+ rounded="pill"
110
+ variant="flat"
111
+ size="small"
112
+ @click="openWorOrderCreateDialog()"
113
+ >
114
+ Create WorkOrder
115
+ </v-btn>
116
+ </v-col>
117
+ </v-row>
118
+
119
+ <v-row dense class="my-1" v-if="type === 'workOrder'">
120
+ <v-col cols="6" class="py-1"
121
+ ><strong>Work Order Number:</strong></v-col
122
+ >
123
+ <v-col v-if="item.workOrderNo" cols="6" class="py-1 text-right"
124
+ >{{ item.workOrderNo }}
125
+ </v-col>
126
+ </v-row>
127
+
128
+ <v-divider />
129
+
130
+ <v-row dense class="my-1">
131
+ <v-col cols="6" class="py-1"><strong>Status:</strong></v-col>
132
+ <v-col cols="6" class="py-1 text-right">
133
+ <v-chip
134
+ :color="getColorStatus(item.status)"
135
+ size="x-small"
136
+ text-color="white"
137
+ >
138
+ {{ item.status }}
139
+ </v-chip>
140
+ </v-col>
141
+ </v-row>
142
+
143
+ <v-divider />
144
+
145
+ <v-row dense class="my-1">
146
+ <v-col cols="6" class="py-1"><strong>Created At:</strong></v-col>
147
+ <v-col cols="6" class="py-1 text-right">{{
148
+ formatDate(item.createdAt)
149
+ }}</v-col>
150
+ </v-row>
151
+ </v-col>
152
+ </v-sheet>
153
+ </v-row>
154
+
155
+ <v-sheet class="px-4 py-2">
156
+ <v-btn
157
+ block
158
+ color="primary"
159
+ variant="flat"
160
+ class="mb-2 text-capitalize"
161
+ @click="$emit('edit')"
162
+ >
163
+ Edit
164
+ </v-btn>
165
+ <v-btn
166
+ v-if="item?.status !== 'Completed'"
167
+ block
168
+ color="primary-button"
169
+ density="default"
170
+ class="mb-2 text-capitalize"
171
+ @click="$emit('mark-complete-request')"
172
+ >
173
+ Mark as Complete
174
+ </v-btn>
175
+ <v-btn
176
+ block
177
+ variant="flat"
178
+ color="red"
179
+ density="default"
180
+ class="text-white text-capitalize"
181
+ @click="$emit('delete')"
182
+ >
183
+ Delete
184
+ </v-btn>
185
+ </v-sheet>
186
+
187
+ <WorkOrderCreate
188
+ v-model="showCreateDialog"
189
+ created-from="feedback"
190
+ :work-order="_workOrder"
191
+ @update:work-order="(val: TWorkOrderCreate) => (_workOrder = val)"
192
+ :is-edit-mode="isEditMode"
193
+ :loading="isSubmitting"
194
+ :categories="serviceProviders"
195
+ :theme="theme"
196
+ :errored-images="erroredImages"
197
+ :max-files="5"
198
+ :message-fn="showMessage"
199
+ @close="handleCloseDialog"
200
+ @file-added="handleFileAdded"
201
+ @file-deleted="deleteFile"
202
+ @submit="submitWorkOrder"
203
+ />
204
+
205
+ <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
206
+ </v-sheet>
207
+ </template>
208
+
209
+ <script setup lang="ts">
210
+ import { useTheme } from "vuetify";
211
+ const { getColorStatus, formatDate } = useUtils();
212
+
213
+ const props = defineProps({
214
+ item: {
215
+ type: Object,
216
+ default: () => ({}),
217
+ },
218
+ senderName: {
219
+ type: String,
220
+ default: "",
221
+ },
222
+ type: {
223
+ type: String,
224
+ default: "feedback",
225
+ },
226
+ });
227
+
228
+ const emit = defineEmits<{
229
+ (e: "update:provider", serviceProvider: string): void;
230
+ (e: "mark-complete-request"): void;
231
+ (e: "delete"): void;
232
+ (e: "edit"): void;
233
+ (e: "updateWorkOrderId", workOrderId: string): void;
234
+ }>();
235
+
236
+ const { currentUser } = useLocalAuth();
237
+ const { getNameInitials } = useUtils();
238
+
239
+ const showCreateDialog = ref(false);
240
+ const isEditMode = ref(false);
241
+ const isSubmitting = ref(false);
242
+ const theme = useTheme().name;
243
+ const erroredImages = ref<string[]>([]);
244
+
245
+ const message = ref("");
246
+ const messageColor = ref("");
247
+ const messageSnackbar = ref(false);
248
+
249
+ const route = useRoute();
250
+
251
+ const { getWorkOrders: _getWorkOrders, createWorkOrder } = useWorkOrder();
252
+
253
+ const _workOrder = ref<TWorkOrderCreate>({
254
+ attachments: [],
255
+ category: "",
256
+ subject: "",
257
+ location: "",
258
+ description: "",
259
+ highPriority: false,
260
+ block: "",
261
+ level: "",
262
+ unit: "",
263
+ serviceProvider: "",
264
+ assignee: "",
265
+ organization: "",
266
+ site: "",
267
+ });
268
+
269
+ const serviceProviders = ref<
270
+ Array<{ title: string; value: string; subtitle: string }>
271
+ >([]);
272
+ // const { getAll: getAllServiceProvider } = useServiceProvider();
273
+
274
+ // const { data: getAllReq } = useLazyAsyncData("get-all-service-providers", () =>
275
+ // getAllServiceProvider({
276
+ // siteId: useRoute().params.site as string,
277
+ // })
278
+ // );
279
+
280
+ // watchEffect(() => {
281
+ // if (getAllReq.value) {
282
+ // serviceProviders.value = getAllReq.value.items.map((i: any) => ({
283
+ // title: i.nature.replace(/_/g, " "),
284
+ // value: i.serviceProviderOrgId,
285
+ // subtitle: i.name,
286
+ // }));
287
+ // }
288
+ // });
289
+
290
+ const { getBySiteAsServiceProvider } = useCustomerSite();
291
+
292
+ const { data: getAllReq } = useLazyAsyncData(
293
+ "get-by-site-as-service-provider",
294
+ () => getBySiteAsServiceProvider(useRoute().params.site as string)
295
+ );
296
+
297
+ watchEffect(() => {
298
+ if (getAllReq.value) {
299
+ serviceProviders.value = getAllReq.value.map((i: any) => ({
300
+ title: i.nature.replace(/_/g, " "),
301
+ subtitle: i.title,
302
+ value: i.nature
303
+ }));
304
+ }
305
+ });
306
+
307
+ function showMessage(msg: string, color: string) {
308
+ message.value = msg;
309
+ messageColor.value = color;
310
+ messageSnackbar.value = true;
311
+ }
312
+
313
+ function handleCloseDialog() {
314
+ resetWorkOrderForm();
315
+ isEditMode.value = false;
316
+ showCreateDialog.value = false;
317
+ }
318
+
319
+ function openWorOrderCreateDialog() {
320
+ let newWorkOrder = {
321
+ ..._workOrder.value,
322
+ attachments:
323
+ props.item.attachments.length > 0 ? props.item.attachments : [],
324
+ subject: props.item.subject ? props.item.subject : "",
325
+ category: props.item.category ? props.item.category : "",
326
+ location: props.item.location ? props.item.location : "",
327
+ };
328
+ _workOrder.value = newWorkOrder;
329
+ showCreateDialog.value = true;
330
+ }
331
+
332
+ function resetWorkOrderForm() {
333
+ _workOrder.value = {
334
+ attachments: [],
335
+ category: "",
336
+ subject: "",
337
+ location: "",
338
+ description: "",
339
+ highPriority: false,
340
+ block: "",
341
+ level: "",
342
+ unit: "",
343
+ serviceProvider: "",
344
+ assignee: "",
345
+ organization: "",
346
+ site: "",
347
+ };
348
+ }
349
+
350
+ const { addFile, deleteFile: _deleteFile } = useFile();
351
+
352
+ const API_DO_STORAGE_ENDPOINT =
353
+ useRuntimeConfig().public.API_DO_STORAGE_ENDPOINT;
354
+
355
+ async function handleFileAdded(file: File) {
356
+ try {
357
+ const res = await addFile(file);
358
+ const uploadedId = res?.id;
359
+ if (uploadedId) {
360
+ const url = `${API_DO_STORAGE_ENDPOINT}/${uploadedId}`;
361
+ _workOrder.value.attachments = _workOrder.value.attachments ?? [];
362
+ _workOrder.value.attachments.push(url);
363
+ }
364
+ } catch (error) {
365
+ console.error("Error uploading file:", error);
366
+ showMessage("Failed to upload file", "error");
367
+ }
368
+ }
369
+
370
+ async function deleteFile(value: string) {
371
+ try {
372
+ await _deleteFile(value);
373
+ _workOrder.value.attachments = (_workOrder.value.attachments ?? []).filter(
374
+ (file) => file !== value
375
+ );
376
+ } catch (error) {
377
+ console.log(error);
378
+ showMessage("Failed to delete file", "error");
379
+ }
380
+ }
381
+
382
+ async function submitWorkOrder() {
383
+ try {
384
+ isSubmitting.value = true;
385
+
386
+ const payload = {
387
+ ..._workOrder.value,
388
+ organization: route.params.org as string,
389
+ site: route.params.site as string,
390
+ };
391
+
392
+ const res = await createWorkOrder(payload);
393
+
394
+ showMessage(res.message, "success");
395
+ showCreateDialog.value = false;
396
+ resetWorkOrderForm();
397
+ emit("updateWorkOrderId", res.workOrderId);
398
+ // getAllReqRefresh();
399
+ } catch (err) {
400
+ showMessage((err as Error).message, "error");
401
+ } finally {
402
+ isSubmitting.value = false;
403
+ }
404
+ }
405
+
406
+ function showWorkOrderInfo(workOrderId: string) {
407
+ const route = useRoute();
408
+ const org = route.params.org;
409
+ const site = route.params.site;
410
+ const id = workOrderId;
411
+ return {
412
+ name: "org-site-work-orders-id",
413
+ params: { org, site, id },
414
+ };
415
+ }
416
+ </script>
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <v-card class="mb-2 pa-2 rounded-lg border-sm" elevation="0">
3
+ <v-row no-gutters align="center">
4
+ <v-col cols="12" sm="4" class="d-flex justify-center align-center">
5
+ <v-img
6
+ :src="image"
7
+ alt="Photo"
8
+ max-height="100"
9
+ width="100%"
10
+ cover
11
+ class="rounded-lg"
12
+ />
13
+ </v-col>
14
+
15
+ <v-col cols="12" sm="8" class="py-2 pe-1">
16
+ <v-row dense>
17
+ <v-col cols="12" class="d-flex align-center flex-wrap">
18
+ <span class="text-caption font-weight-medium me-1">Created by</span>
19
+
20
+ <v-avatar color="surface-variant" size="20" class="me-1">
21
+ <v-img
22
+ v-if="userProfile"
23
+ :src="userProfile"
24
+ width="20"
25
+ height="20"
26
+ />
27
+ <span v-else class="text-overline">
28
+ {{ getNameInitials }}
29
+ </span>
30
+ </v-avatar>
31
+
32
+ <span class="text-caption">{{ createdByName || "Unknown" }}</span>
33
+ </v-col>
34
+
35
+ <v-col cols="12" class="text-caption">Subject: {{ subject }}</v-col>
36
+
37
+ <v-col cols="12" class="text-caption">
38
+ Status: <span class="text-orange">{{ status }}</span>
39
+ </v-col>
40
+
41
+ <v-col cols="12" class="text-caption">
42
+ Date Created: {{ createdAt }}
43
+ </v-col>
44
+ </v-row>
45
+ </v-col>
46
+ </v-row>
47
+ </v-card>
48
+ </template>
49
+
50
+ <script setup lang="ts">
51
+ const { getNameInitials } = useUtils();
52
+
53
+ defineProps({
54
+ image: String,
55
+ subject: String,
56
+ status: String,
57
+ createdAt: String,
58
+ createdByName: String,
59
+ userProfile: String,
60
+ getNameInitials: Function,
61
+ });
62
+ </script>
@@ -0,0 +1,158 @@
1
+ <template>
2
+ <v-sheet
3
+ elevation="1"
4
+ rounded="0"
5
+ class="fill-height d-flex flex-column"
6
+ style="border-right: 1px solid #e0e0e0; border-top: 1px solid #e0e0e0"
7
+ >
8
+ <div style="border-bottom: 1px solid #e0e0e0" class="px-4 pt-4 pb-2 mb-2">
9
+ <h3 class="text-h6">
10
+ {{ type === "workOrder" ? "Work Order" : "Feedback" }} Chat
11
+ </h3>
12
+ </div>
13
+
14
+ <v-sheet
15
+ class="overflow-y-auto flex-grow-1 pa-3"
16
+ :style="{ height: 'calc(100vh - 300px)', overflowX: 'hidden' }"
17
+ >
18
+ <v-row
19
+ v-for="(msg, i) in messages"
20
+ :key="i"
21
+ no-gutters
22
+ :justify="msg.isCurrentUser ? 'end' : 'start'"
23
+ class="mb-2"
24
+ >
25
+ <v-col cols="auto">
26
+ <ChatBubbles
27
+ :isCurrentUser="msg.isCurrentUser"
28
+ :message="msg.message"
29
+ :timestamp="msg.timestamp"
30
+ :seenLabel="msg.seenLabel"
31
+ :senderName="msg.senderName"
32
+ :role="msg.role"
33
+ />
34
+ </v-col>
35
+ </v-row>
36
+ </v-sheet>
37
+
38
+ <v-card class="mx-4 mb-4 mt-2 pa-3 rounded-lg" elevation="2">
39
+ <v-row no-gutters align="end">
40
+ <v-col cols="10">
41
+ <v-textarea
42
+ label="Message"
43
+ auto-grow
44
+ hide-details
45
+ rows="1"
46
+ variant="outlined"
47
+ />
48
+ </v-col>
49
+ <v-col cols="2" class="pl-2">
50
+ <v-btn color="primary-button" block class="pa-4 py-6 text-capitalize"
51
+ >Send</v-btn
52
+ >
53
+ </v-col>
54
+ </v-row>
55
+
56
+ <v-divider class="my-2" />
57
+
58
+ <v-row justify="start" no-gutters align="center">
59
+ <v-col cols="auto">
60
+ <v-icon size="20" class="mr-3">mdi-paperclip</v-icon>
61
+ </v-col>
62
+
63
+ <v-col cols="auto">
64
+ <v-icon size="20" class="mr-3">mdi-emoticon-happy-outline</v-icon>
65
+ </v-col>
66
+
67
+ <v-col cols="auto">
68
+ <v-menu location="top" offset="10">
69
+ <template #activator="{ props }">
70
+ <v-btn icon v-bind="props" density="compact" variant="text">
71
+ <v-icon size="20" color="gray">mdi-message-outline</v-icon>
72
+ </v-btn>
73
+ </template>
74
+
75
+ <v-card min-width="350" class="rounded-lg">
76
+ <v-card-title class="text-subtitle-1 font-weight-medium pb-1"
77
+ >Quick Chat</v-card-title
78
+ >
79
+ <v-divider class="mb-1" />
80
+
81
+ <v-list density="compact">
82
+ <v-list-item
83
+ v-for="(option, index) in quickChatOptions"
84
+ :key="index"
85
+ @click="selectQuickChat(option)"
86
+ >
87
+ <v-list-item-title>{{ option }}</v-list-item-title>
88
+ </v-list-item>
89
+ </v-list>
90
+ </v-card>
91
+ </v-menu>
92
+ </v-col>
93
+ </v-row>
94
+ </v-card>
95
+ </v-sheet>
96
+ </template>
97
+
98
+ <script setup lang="ts">
99
+ defineProps({
100
+ type: {
101
+ type: String,
102
+ default: "feedback",
103
+ },
104
+ });
105
+
106
+ const messages = [
107
+ {
108
+ isCurrentUser: true,
109
+ message: "Hello we would like to transfer this task...",
110
+ timestamp: "06/28/2024 08:40",
111
+ seenLabel: "Seen",
112
+ senderName: "You",
113
+ role: "Site Personnel",
114
+ },
115
+ {
116
+ isCurrentUser: false,
117
+ message: "Sure. Let me take care of that.",
118
+ timestamp: "06/28/2024 08:42",
119
+ senderName: "Tan Wei",
120
+ role: "Manager",
121
+ },
122
+ {
123
+ isCurrentUser: true,
124
+ message: "Thanks!",
125
+ timestamp: "06/28/2024 08:43",
126
+ seenLabel: "Seen",
127
+ senderName: "You",
128
+ role: "Site Personnel",
129
+ },
130
+ {
131
+ isCurrentUser: false,
132
+ message: "No worries!",
133
+ timestamp: "06/28/2024 08:44",
134
+ senderName: "Tan Wei",
135
+ role: "Manager",
136
+ },
137
+ {
138
+ isCurrentUser: true,
139
+ message: "Let me know if you need anything else.",
140
+ timestamp: "06/28/2024 08:45",
141
+ seenLabel: "Seen",
142
+ senderName: "You",
143
+ role: "Site Personnel",
144
+ },
145
+ ];
146
+
147
+ const quickChatOptions = [
148
+ "This is all noted, thank you!",
149
+ "Ok, we are working on it.",
150
+ "Yes, we will do it as soon as possible.",
151
+ "Sorry, we cannot make it this time.",
152
+ "We are here to help.",
153
+ ];
154
+
155
+ const selectQuickChat = (msg: string) => {
156
+ console.log("Quick Chat Selected:", msg);
157
+ };
158
+ </script>