@7365admin1/layer-common 3.1.2-staging.31 → 3.1.2-staging.33

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.
@@ -171,7 +171,7 @@ async function exportReport(type: "pdf" | "csv") {
171
171
  const csv = Array.from(table.querySelectorAll("tr"))
172
172
  .map((row) =>
173
173
  Array.from(row.querySelectorAll("th,td"))
174
- .map((cell) => `"${cell.textContent?.trim().replace(/"/g, '""') ?? ""}"`)
174
+ .map((cell) => escapeCsvCell(getCsvCellValue(cell)))
175
175
  .join(","),
176
176
  )
177
177
  .join("\n");
@@ -188,4 +188,22 @@ async function exportReport(type: "pdf" | "csv") {
188
188
 
189
189
  URL.revokeObjectURL(link.href);
190
190
  }
191
+
192
+ function getCsvCellValue(cell: Element) {
193
+ const image = cell.querySelector("img");
194
+
195
+ if (image) {
196
+ const imageUrl = image.currentSrc || image.src;
197
+
198
+ if (imageUrl) {
199
+ return imageUrl;
200
+ }
201
+ }
202
+
203
+ return cell.textContent?.trim() ?? "";
204
+ }
205
+
206
+ function escapeCsvCell(value: string) {
207
+ return `"${value.replace(/"/g, '""')}"`;
208
+ }
191
209
  </script>
@@ -7,6 +7,8 @@
7
7
  <thead>
8
8
  <tr>
9
9
  <th>Checkpoints</th>
10
+ <th>Security Personnel</th>
11
+ <th>Signature</th>
10
12
  <th>Start - End Time</th>
11
13
  <th>
12
14
  Travel time<br />
@@ -23,6 +25,17 @@
23
25
  @click="handleActivityClick(activity)"
24
26
  >
25
27
  <td>{{ activity.checkpoint }}</td>
28
+ <td>{{ activity.guardName || "-" }}</td>
29
+ <td>
30
+ <v-img
31
+ v-if="activity.guardSignatureFileId"
32
+ :src="getFileUrl(activity.guardSignatureFileId)"
33
+ :alt="`${activity.guardName || 'Guard'} signature`"
34
+ class="guard-signature"
35
+ contain
36
+ />
37
+ <span v-else>-</span>
38
+ </td>
26
39
  <td>
27
40
  <v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
28
41
  {{ activity.startTime }}
@@ -59,6 +72,8 @@ defineProps<{
59
72
  activities: NFCPatrolActivity[];
60
73
  }>();
61
74
 
75
+ const { getFileUrl } = useFile();
76
+
62
77
  const emit = defineEmits<{
63
78
  remarks: [activity: NFCPatrolActivity];
64
79
  }>();
@@ -78,4 +93,9 @@ function handleActivityClick(activity: NFCPatrolActivity) {
78
93
  .cursor-pointer {
79
94
  cursor: pointer;
80
95
  }
96
+
97
+ .guard-signature {
98
+ width: 80px;
99
+ height: 40px;
100
+ }
81
101
  </style>
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <div class="d-flex flex-column">
3
+ <v-text-field
4
+ v-bind="$attrs"
5
+ :model-value="formattedDate"
6
+ autocomplete="off"
7
+ placeholder="DD/MM/YY"
8
+ readonly
9
+ @click="openDatePicker"
10
+ >
11
+ <template #append-inner>
12
+ <v-icon icon="mdi-calendar" @click.stop="openDatePicker" />
13
+ </template>
14
+ </v-text-field>
15
+
16
+ <input
17
+ ref="dateInput"
18
+ v-model="date"
19
+ class="native-date-input"
20
+ type="date"
21
+ />
22
+ </div>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ defineOptions({ inheritAttrs: false });
27
+
28
+ const date = defineModel<string>({ default: "" });
29
+ const dateInput = ref<HTMLInputElement | null>(null);
30
+
31
+ const formattedDate = computed(() => {
32
+ const match = date.value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
33
+
34
+ if (!match) return "";
35
+
36
+ const [, year, month, day] = match;
37
+ return `${day}/${month}/${year.slice(-2)}`;
38
+ });
39
+
40
+ function openDatePicker() {
41
+ const input = dateInput.value;
42
+
43
+ if (!input) return;
44
+
45
+ if (input.showPicker) {
46
+ input.showPicker();
47
+ } else {
48
+ input.click();
49
+ }
50
+ }
51
+ </script>
52
+
53
+ <style scoped>
54
+ .native-date-input {
55
+ position: absolute;
56
+ width: 1px;
57
+ height: 1px;
58
+ opacity: 0;
59
+ pointer-events: none;
60
+ }
61
+ </style>
@@ -41,9 +41,8 @@
41
41
  md="2"
42
42
  class="px-md-2 mb-2 mb-md-0"
43
43
  >
44
- <v-text-field
44
+ <ReportDatePicker
45
45
  :model-value="modelValue.date"
46
- type="date"
47
46
  variant="outlined"
48
47
  density="comfortable"
49
48
  rounded="lg"
@@ -90,6 +89,7 @@ import type {
90
89
  NFCPatrolReportFilterOptions,
91
90
  NFCPatrolReportFilters,
92
91
  } from "../../../types/nfc-patrol-report";
92
+ import ReportDatePicker from "./ReportDatePicker.vue";
93
93
 
94
94
  const props = withDefaults(
95
95
  defineProps<{
@@ -8,6 +8,8 @@
8
8
  <tr>
9
9
  <th>Patrol Route Name</th>
10
10
  <th>Security Check Schedule</th>
11
+ <th>Security Personnel</th>
12
+ <th>Signature</th>
11
13
  <th>Checked</th>
12
14
  <th>Missed</th>
13
15
  <th>Status</th>
@@ -20,6 +22,17 @@
20
22
  <v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
21
23
  {{ row.securityCheckSchedule }}
22
24
  </td>
25
+ <td>{{ row.guardName || "-" }}</td>
26
+ <td>
27
+ <v-img
28
+ v-if="row.guardSignatureFileId"
29
+ :src="getFileUrl(row.guardSignatureFileId)"
30
+ :alt="`${row.guardName || 'Guard'} signature`"
31
+ class="guard-signature"
32
+ contain
33
+ />
34
+ <span v-else>-</span>
35
+ </td>
23
36
  <td>{{ row.checked }}</td>
24
37
  <td>{{ row.missed }}</td>
25
38
  <td>
@@ -45,7 +58,16 @@ defineProps<{
45
58
  rows: NFCPatrolReportSummaryRow[];
46
59
  }>();
47
60
 
61
+ const { getFileUrl } = useFile();
62
+
48
63
  function statusClass(status: NFCPatrolReportSummaryStatus) {
49
64
  return status === "Completed" ? "text-success" : "text-error";
50
65
  }
51
66
  </script>
67
+
68
+ <style scoped>
69
+ .guard-signature {
70
+ width: 80px;
71
+ height: 40px;
72
+ }
73
+ </style>
@@ -825,10 +825,8 @@
825
825
  item-title="label"
826
826
  item-value="value"
827
827
  v-model="pass.status"
828
- :disabled="
829
- selectedVisitorObject.checkOut || pass.status == 'Removed'
830
- "
831
- ></v-select>
828
+ :disabled="pass.status == 'Removed'"
829
+ />
832
830
  <v-textarea
833
831
  v-if="pass.status === 'Lost' || pass.status === 'Damaged'"
834
832
  no-resize
@@ -836,8 +834,7 @@
836
834
  class="w-100"
837
835
  density="compact"
838
836
  v-model="pass.remarks"
839
- :disabled="selectedVisitorObject.checkOut"
840
- ></v-textarea>
837
+ />
841
838
  </v-row>
842
839
  </div>
843
840
  <div v-if="keyReturnStatuses.length > 0" class="mb-2">
@@ -864,10 +861,8 @@
864
861
  item-title="label"
865
862
  item-value="value"
866
863
  v-model="key.status"
867
- :disabled="
868
- selectedVisitorObject.checkOut || key.status == 'Removed'
869
- "
870
- ></v-select>
864
+ :disabled="key.status == 'Removed'"
865
+ />
871
866
  <v-textarea
872
867
  v-if="key.status === 'Lost' || key.status === 'Damaged'"
873
868
  no-resize
@@ -875,8 +870,7 @@
875
870
  class="w-100"
876
871
  density="compact"
877
872
  v-model="key.remarks"
878
- :disabled="selectedVisitorObject.checkOut"
879
- ></v-textarea>
873
+ />
880
874
  </v-row>
881
875
  </div>
882
876
 
@@ -886,11 +880,11 @@
886
880
  color="blue"
887
881
  density="comfortable"
888
882
  class="text-capitalize"
889
- :disabled="selectedVisitorObject.checkOut"
890
883
  :loading="loading.updatingPassKeys"
891
884
  @click.stop="handleUpdatePassKeys"
892
- >Update Pass/Keys</v-btn
893
885
  >
886
+ Update Pass/Keys
887
+ </v-btn>
894
888
  </v-row>
895
889
  </v-card-text>
896
890
  <v-toolbar class="pa-0" density="compact">
@@ -900,8 +894,9 @@
900
894
  variant="text"
901
895
  block
902
896
  @click="dialog.returnPassesKeys = false"
903
- >Close</v-btn
904
897
  >
898
+ Close
899
+ </v-btn>
905
900
  </v-col>
906
901
  <v-col cols="6">
907
902
  <v-btn
@@ -915,8 +910,9 @@
915
910
  !canConfirmCheckout || selectedVisitorObject.checkOut
916
911
  "
917
912
  @click="proceedCheckout"
918
- >Confirm Checkout</v-btn
919
913
  >
914
+ Confirm Checkout
915
+ </v-btn>
920
916
  </v-col>
921
917
  </v-row>
922
918
  </v-toolbar>
@@ -34,6 +34,8 @@ function mapLogToSummaryRow(log: Record<string, any>, index: number): NFCPatrolS
34
34
  id: log._id,
35
35
  routeName: `${index + 1}. ${log.route?.name ?? "-"}`,
36
36
  securityCheckSchedule: log.route?.startTime ?? "-",
37
+ guardName: log.sign?.guardName,
38
+ guardSignatureFileId: log.sign?.guardSignatureFileId,
37
39
  checked,
38
40
  missed,
39
41
  status: missed === 0 ? "Completed" : "Incomplete",
@@ -51,6 +51,8 @@ function mapLogToPatrolReport(
51
51
  const activities: NFCPatrolActivity[] = checkpoints.map((cp) => ({
52
52
  id: cp.nfcTag_id,
53
53
  checkpoint: cp.nfcTag_name,
54
+ guardName: log.sign?.guardName,
55
+ guardSignatureFileId: log.sign?.guardSignatureFileId,
54
56
  startTime: formatTime(cp.startDateTime),
55
57
  endTime: formatTime(cp.endDateTime),
56
58
  actualTime: diffMinutes(cp.startDateTime, cp.endDateTime),
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "3.1.2-staging.31",
5
+ "version": "3.1.2-staging.33",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {
@@ -47,6 +47,8 @@ export interface NFCPatrolRouteSummary {
47
47
  export interface NFCPatrolActivity {
48
48
  id: string;
49
49
  checkpoint: string;
50
+ guardName?: string;
51
+ guardSignatureFileId?: string;
50
52
  startTime: string;
51
53
  endTime: string;
52
54
  actualTime: string;
@@ -67,6 +69,8 @@ export interface NFCPatrolReportSummaryRow {
67
69
  id: string;
68
70
  routeName: string;
69
71
  securityCheckSchedule: string;
72
+ guardName?: string;
73
+ guardSignatureFileId?: string;
70
74
  checked: number;
71
75
  missed: number;
72
76
  status: NFCPatrolReportSummaryStatus;