@7365admin1/layer-common 1.11.23 → 1.11.26

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.
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <v-dialog :model-value="modelValue" :max-width="fileType === 'image' ? '1200' : '900'" scrollable
3
+ @update:model-value="emit('update:modelValue', $event)">
4
+ <v-card>
5
+ <v-toolbar density="compact">
6
+ <v-toolbar-title class="text-subtitle-2 text-truncate">{{ fileName }}</v-toolbar-title>
7
+ <v-spacer />
8
+ <v-btn icon="mdi-open-in-new" variant="text" size="small" @click="openInNewTab()" />
9
+ <v-btn icon="mdi-close" variant="text" size="small" @click="emit('update:modelValue', false)" />
10
+ </v-toolbar>
11
+ <v-card-text class="pa-0"
12
+ :style="{ height: fileType === 'image' ? 'auto' : '75vh', 'overflow-y': fileType === 'image' ? 'auto' : 'auto' }">
13
+ <!-- Image Viewer -->
14
+ <v-img v-if="fileType === 'image'" :src="getFileUrl(fileId)" :alt="fileName" class="w-100" />
15
+
16
+ <!-- Video Viewer -->
17
+ <video v-else-if="fileType === 'video'" width="100%" height="100%" controls
18
+ style="background-color: #000;">
19
+ <source :src="getFileUrl(fileId)" :type="mimeType" />
20
+ Your browser does not support the video tag.
21
+ </video>
22
+
23
+ <!-- Document Viewer (PDF, Word, etc.) -->
24
+ <iframe v-else :src="getFileUrl(fileId)" width="100%" height="100%" style="border: none;"
25
+ :title="fileName" />
26
+ </v-card-text>
27
+ <v-card-actions>
28
+ <v-spacer />
29
+ <v-btn variant="text" class="text-none" @click="openInNewTab()">
30
+ <v-icon start>mdi-download</v-icon>
31
+ Open / Download
32
+ </v-btn>
33
+ </v-card-actions>
34
+ </v-card>
35
+ </v-dialog>
36
+ </template>
37
+
38
+ <script setup lang="ts">
39
+ const props = defineProps({
40
+ modelValue: {
41
+ type: Boolean,
42
+ default: false,
43
+ },
44
+ fileId: {
45
+ type: String,
46
+ required: true,
47
+ },
48
+ fileName: {
49
+ type: String,
50
+ default: "Document",
51
+ },
52
+ mimeType: {
53
+ type: String,
54
+ default: "application/octet-stream",
55
+ },
56
+ });
57
+
58
+ const emit = defineEmits(['update:modelValue']);
59
+
60
+ const { getFileUrl } = useFile();
61
+
62
+ const fileType = computed(() => {
63
+ if (props.mimeType.startsWith('image/')) {
64
+ return 'image';
65
+ } else if (props.mimeType.startsWith('video/')) {
66
+ return 'video';
67
+ } else {
68
+ return 'document';
69
+ }
70
+ });
71
+
72
+ function openInNewTab() {
73
+ window.open(getFileUrl(props.fileId), '_blank', 'noopener,noreferrer');
74
+ }
75
+ </script>
@@ -441,7 +441,6 @@
441
441
 
442
442
  <script setup lang="ts">
443
443
  import useEquipmentItem from "../composables/useEquipmentItem";
444
- import { useEquipmentItemPermission } from "../composables/useEquipmentItemPermission";
445
444
  import useEquipmentManagement from "../composables/useEquipmentManagement";
446
445
  import useFile from "../composables/useFile";
447
446
  import useUtils from "../composables/useUtils";
@@ -450,9 +449,12 @@ const props = defineProps({
450
449
  orgId: { type: String, default: "" },
451
450
  site: { type: String, default: "" },
452
451
  serviceType: { type: String, default: "" },
452
+ canViewAllEquipmentItems: { type: Boolean, default: true },
453
+ canCreateEquipmentItem: { type: Boolean, default: false },
453
454
  });
454
455
 
455
- const { canCreateEquipmentItem } = useEquipmentItemPermission();
456
+ const canViewAllEquipmentItems = computed(() => props.canViewAllEquipmentItems);
457
+ const canCreateEquipmentItem = computed(() => props.canCreateEquipmentItem);
456
458
 
457
459
  const items = ref<Array<Record<string, any>>>([]);
458
460
  const siteName = ref<string>("");
@@ -247,7 +247,6 @@
247
247
  <script setup lang="ts">
248
248
  import useEquipment from "../composables/useEquipment";
249
249
  import useStock from "../composables/useStock";
250
- import { useEquipmentPermission } from "../composables/useEquipmentPermission";
251
250
  import useUtils from "../composables/useUtils";
252
251
  import useEquipmentManagement from "../composables/useEquipmentManagement";
253
252
 
@@ -255,17 +254,22 @@ const props = defineProps({
255
254
  orgId: { type: String, default: "" },
256
255
  site: { type: String, default: "" },
257
256
  serviceType: { type: String, default: "" },
257
+ canViewEquipments: { type: Boolean, default: true },
258
+ canAddEquipment: { type: Boolean, default: false },
259
+ canUpdateEquipment: { type: Boolean, default: false },
260
+ canDeleteEquipment: { type: Boolean, default: false },
261
+ canAddStock: { type: Boolean, default: false },
262
+ canViewStock: { type: Boolean, default: true },
263
+ canRequestItem: { type: Boolean, default: false },
258
264
  });
259
265
 
260
- const {
261
- canViewEquipments,
262
- canAddEquipment,
263
- canUpdateEquipment,
264
- canDeleteEquipment,
265
- canAddStock,
266
- canViewStock,
267
- canRequestItem,
268
- } = useEquipmentPermission();
266
+ const canViewEquipments = computed(() => props.canViewEquipments);
267
+ const canAddEquipment = computed(() => props.canAddEquipment);
268
+ const canUpdateEquipment = computed(() => props.canUpdateEquipment);
269
+ const canDeleteEquipment = computed(() => props.canDeleteEquipment);
270
+ const canAddStock = computed(() => props.canAddStock);
271
+ const canViewStock = computed(() => props.canViewStock);
272
+ const canRequestItem = computed(() => props.canRequestItem);
269
273
 
270
274
  const items = ref<Array<Record<string, any>>>([]);
271
275
  const siteName = ref<string>("");
@@ -17,7 +17,7 @@
17
17
  </v-col>
18
18
  </v-row>
19
19
  <TableHygiene
20
- :title="'Cleaner Checklist'"
20
+ :title="scheduleTitle"
21
21
  :headers="headers"
22
22
  :items="items"
23
23
  :item-value="'unit'"
@@ -319,6 +319,18 @@ const props = defineProps({
319
319
  const { getUnitCleanerChecklist, updateUnitChecklist } = useCleaningSchedules();
320
320
  const { getBySiteAsServiceProvider } = useCustomerSite();
321
321
  const { back } = useUtils();
322
+
323
+ const route = useRoute();
324
+ const scheduleTitle = computed(() => {
325
+ const path = route.path;
326
+ if (path.includes('cleaning-schedule')) return 'Cleaning Checklist';
327
+ if (path.includes('technician-schedule')) return 'Technician Checklist';
328
+ if (path.includes('landscape-schedule')) return 'Landscape Checklist';
329
+ if (path.includes('pool-schedule')) return 'Pool Checklist';
330
+ if (path.includes('pest-schedule')) return 'Pest Checklist';
331
+ return 'Cleaner Checklist';
332
+ });
333
+
322
334
  const canAddRemarks = computed(() => props.canAddRemarks);
323
335
  const canManageScheduleTasks = computed(() => props.canManageScheduleTasks);
324
336
 
@@ -32,7 +32,7 @@
32
32
  <!-- This is where the QR code scanner will be displayed -->
33
33
  </div>
34
34
  <!-- Show Error Messages -->
35
- <div v-if="showNotFoundMessage" class="text-error mt-4">
35
+ <div v-if="showNotFoundMessage" class="text-error mt-4 text-h5">
36
36
  <p>{{ errorMessage }}</p>
37
37
  </div>
38
38
  <!-- Switch camera button inside the reader box -->
@@ -58,6 +58,8 @@ const props = defineProps({
58
58
  },
59
59
  });
60
60
 
61
+ const { standardFormatDate } = useUtils();
62
+
61
63
  const { validateVisitorQrCode } = useVisitor();
62
64
 
63
65
  const emits = defineEmits([
@@ -109,9 +111,14 @@ const handleScanVisitorQRCode = async () => {
109
111
  console.log("validateVisitorQrCode", response);
110
112
  if (response.errorMessage) {
111
113
  showNotFoundMessage.value = true;
112
- errorMessage.value = response.errorMessage;
114
+ errorMessage.value =
115
+ response.errorMessage +
116
+ (response.scheduledDate
117
+ ? standardFormatDate(response.scheduledDate)
118
+ : "");
113
119
  showMessage(errorMessage.value, "error");
114
120
  } else {
121
+ closeDialog();
115
122
  emits("openVisitorDataFromScannedQrCodeDialog", response);
116
123
  }
117
124
  } else {
@@ -192,7 +192,6 @@ const headers = [
192
192
  { title: "Status", value: "status" },
193
193
  { title: "Type", value: "type" },
194
194
  { title: "No. of Visits", value: "set" },
195
- { title: "Created By", value: "createdByName" },
196
195
  ];
197
196
 
198
197
  const { formatDate, back } = useUtils();
@@ -28,7 +28,7 @@
28
28
  />
29
29
  </v-col>
30
30
 
31
- <v-col cols="12">
31
+ <v-col v-if="!visitorInfo.checkIn" cols="12">
32
32
  <InputLabel class="text-capitalize" title="NRIC/Passport/ID No." />
33
33
  <v-text-field
34
34
  v-if="!visitorInfo.checkIn"
@@ -46,7 +46,7 @@
46
46
  />
47
47
  </v-col>
48
48
 
49
- <v-col cols="12" class="mt-4">
49
+ <v-col v-if="!visitorInfo.checkIn" cols="12" class="mt-4">
50
50
  <InputLabel
51
51
  class="text-capitalize"
52
52
  title="Vehicle Number (Optional)"
@@ -67,7 +67,7 @@
67
67
  />
68
68
  </v-col>
69
69
 
70
- <v-col cols="12" class="mt-4">
70
+ <!-- <v-col cols="12" class="mt-4">
71
71
  <InputLabel class="text-capitalize" title="Type" />
72
72
  <v-text-field
73
73
  v-model="visitorInfo.type"
@@ -75,9 +75,9 @@
75
75
  hide-details
76
76
  readonly
77
77
  />
78
- </v-col>
78
+ </v-col> -->
79
79
 
80
- <v-col cols="12" class="mt-4">
80
+ <v-col v-if="!visitorInfo.checkIn" cols="12" class="mt-4">
81
81
  <InputLabel class="text-capitalize" title="Phone Number" />
82
82
  <InputPhoneNumberV2
83
83
  v-model="visitorInfo.contact"
@@ -87,32 +87,62 @@
87
87
  </v-col>
88
88
 
89
89
  <v-col cols="12">
90
+ <InputLabel class="text-capitalize" title="Location" />
91
+ <v-text-field v-model="location" density="comfortable" readonly />
92
+ </v-col>
93
+
94
+ <v-col v-if="!visitorInfo.checkIn" cols="12">
90
95
  <InputLabel class="text-capitalize" title="Purpose" />
91
96
  <v-textarea
92
97
  v-model="visitorInfo.purpose"
93
98
  density="comfortable"
94
- :rows="3"
99
+ :rows="2"
95
100
  no-resize
96
101
  readonly
97
102
  />
98
103
  </v-col>
99
104
 
100
- <v-col cols="12">
101
- <InputLabel class="text-capitalize" title="Location" />
102
- <v-text-field v-model="location" density="comfortable" />
105
+ <v-col v-if="!visitorInfo.checkIn" cols="12">
106
+ <InputLabel class="text-capitalize" title="Remarks" />
107
+ <v-textarea
108
+ v-model="visitorInfo.remarks"
109
+ density="comfortable"
110
+ :rows="2"
111
+ no-resize
112
+ />
103
113
  </v-col>
104
114
 
105
- <v-col cols="12">
115
+ <v-col v-if="!visitorInfo.checkIn" cols="12">
106
116
  <InputLabel class="text-capitalize" title="Visitor Pass" />
107
117
  <PassInformation
108
118
  v-model:pass="visitorInfo.visitorPass"
109
119
  :site="prop.site"
110
120
  :type="visitorInfo.type"
121
+ :hideKeys="true"
111
122
  :clearable="true"
112
123
  />
113
124
  </v-col>
114
125
 
115
- <v-col cols="12" class="mt-4">
126
+ <v-col v-else cols="12">
127
+ <v-card elevation="4" height="100%" width="100%">
128
+ <v-col cols="12" class="d-flex justify-center pb-0">
129
+ <span
130
+ class="text-h6 font-weight-bold d-flex text-center text-success"
131
+ >
132
+ {{
133
+ visitorInfo.checkIn
134
+ ? standardFormatDateTime(visitorInfo.checkIn)
135
+ : "N/A"
136
+ }}
137
+ </span>
138
+ </v-col>
139
+ <v-col cols="12" class="d-flex justify-center">
140
+ Actual Check In
141
+ </v-col>
142
+ </v-card>
143
+ </v-col>
144
+
145
+ <!-- <v-col cols="12" class="mt-4">
116
146
  <v-row no-gutters class="pt-2">
117
147
  <v-col cols="6" class="pr-1">
118
148
  <v-card elevation="3" height="100%" width="100%">
@@ -151,7 +181,7 @@
151
181
  </v-card>
152
182
  </v-col>
153
183
  </v-row>
154
- </v-col>
184
+ </v-col> -->
155
185
  </v-row>
156
186
  <!-- </v-form> -->
157
187
  </v-card-text>
@@ -177,7 +207,7 @@
177
207
  <v-btn
178
208
  v-if="visitorInfo.checkIn && !visitorInfo.checkOut"
179
209
  block
180
- text="Check Out"
210
+ text="Confirm Check Out"
181
211
  variant="flat"
182
212
  color="error"
183
213
  size="48"
@@ -229,13 +259,13 @@ const visitorPlateNumber = computed({
229
259
  set: () => {}, // readonly, do nothing on set
230
260
  });
231
261
 
232
- watchEffect(() => {
233
- if (visitorInfo.value.plateNumber) {
234
- visitorInfo.value.type = "guest";
235
- } else {
236
- visitorInfo.value.type = "walk-in";
237
- }
238
- });
262
+ // watchEffect(() => {
263
+ // if (visitorInfo.value.plateNumber) {
264
+ // visitorInfo.value.type = "guest";
265
+ // } else {
266
+ // visitorInfo.value.type = "walk-in";
267
+ // }
268
+ // });
239
269
 
240
270
  const location = computed({
241
271
  get: () => {
@@ -475,15 +475,13 @@ async function handleAddNewContractorType() {
475
475
 
476
476
  async function handleSelectCompanyName(company: string) {
477
477
  visitor.company = company
478
+ // const selected = companyAutofillDataArray.value.find(x => x.companyName?.includes(company))
479
+ // if (!selected) return
478
480
 
479
- const selected = companyAutofillDataArray.value.find(x => x.companyName?.includes(company))
480
- if (!selected) return
481
-
482
- visitor.block = selected.block || ""
483
- visitor.level = selected.level || ""
484
- visitor.unit = selected.unit || ""
485
- visitor.unitName = selected.unitName || ""
486
-
481
+ // visitor.block = selected.block || ""
482
+ // visitor.level = selected.level || ""
483
+ // visitor.unit = selected.unit || ""
484
+ // visitor.unitName = selected.unitName || ""
487
485
  }
488
486
 
489
487
  const companyCombo = ref(null)