@7365admin1/layer-common 1.10.7 → 1.10.9

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 (49) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components/AccessCardAddForm.vue +1 -1
  3. package/components/AccessCardAssignToUnitForm.vue +1 -1
  4. package/components/AccessManagement.vue +1 -1
  5. package/components/{AddSupplyForm.vue → AddEqupmentForm.vue} +5 -5
  6. package/components/BuildingManagement/units.vue +2 -2
  7. package/components/BuildingUnitFormAdd.vue +4 -4
  8. package/components/BuildingUnitFormEdit.vue +114 -68
  9. package/components/Carousel.vue +474 -0
  10. package/components/DrawImage.vue +172 -0
  11. package/components/EntryPassInformation.vue +283 -29
  12. package/components/{CheckoutItemMain.vue → EquipmentItemMain.vue} +95 -87
  13. package/components/{SupplyManagement.vue → EquipmentManagement.vue} +3 -3
  14. package/components/Feedback/Form.vue +4 -4
  15. package/components/FeedbackMain.vue +748 -145
  16. package/components/FileInput.vue +289 -0
  17. package/components/Input/DateTimePicker.vue +17 -11
  18. package/components/ManageChecklistMain.vue +379 -41
  19. package/components/StockCard.vue +11 -7
  20. package/components/TableHygiene.vue +42 -452
  21. package/components/UnitPersonCard.vue +74 -14
  22. package/components/VisitorForm.vue +193 -52
  23. package/components/VisitorFormSelection.vue +13 -2
  24. package/components/VisitorManagement.vue +83 -55
  25. package/composables/useAccessManagement.ts +41 -18
  26. package/composables/useCleaningPermission.ts +7 -7
  27. package/composables/useDashboardData.ts +2 -2
  28. package/composables/useEquipment.ts +63 -0
  29. package/composables/{useCheckout.ts → useEquipmentItem.ts} +7 -7
  30. package/composables/{useCheckoutPermission.ts → useEquipmentItemPermission.ts} +13 -13
  31. package/composables/{useSupply.ts → useEquipmentManagement.ts} +1 -1
  32. package/composables/useEquipmentManagementPermission.ts +96 -0
  33. package/composables/{useSupplyPermission.ts → useEquipmentPermission.ts} +9 -9
  34. package/composables/useFeedback.ts +53 -21
  35. package/composables/useLocalAuth.ts +29 -1
  36. package/composables/useUploadFiles.ts +94 -0
  37. package/composables/useUtils.ts +152 -53
  38. package/composables/useVehicle.ts +21 -2
  39. package/composables/useVisitor.ts +9 -7
  40. package/composables/useWorkOrder.ts +25 -3
  41. package/package.json +2 -1
  42. package/types/building.d.ts +1 -1
  43. package/types/{checkout-item.d.ts → equipment-item.d.ts} +3 -3
  44. package/types/{supply.d.ts → equipment.d.ts} +2 -2
  45. package/types/feedback.d.ts +5 -2
  46. package/types/people.d.ts +3 -1
  47. package/types/user.d.ts +1 -0
  48. package/types/vehicle.d.ts +2 -0
  49. package/types/visitor.d.ts +2 -1
@@ -0,0 +1,289 @@
1
+ <template>
2
+ <div>
3
+ <v-file-input
4
+ v-model="files"
5
+ :label="label"
6
+ accept="image/*"
7
+ :prepend-icon="prependIcon"
8
+ :loading="isFileUploading"
9
+ hide-details
10
+ chips
11
+ multiple
12
+ clearable
13
+ @update:modelValue="handleFileSelect"
14
+ @click:clear="handleClear"
15
+ :hide-input="hasHideInput"
16
+ >
17
+ <template v-slot:append v-if="hasLabel">
18
+ <slot name="append">
19
+ <v-btn color="primary" height="50px" @click="openCameraDialog">
20
+ <v-icon>mdi-camera</v-icon>
21
+ </v-btn>
22
+ </slot>
23
+ </template>
24
+ </v-file-input>
25
+
26
+ <!-- Camera Dialog -->
27
+ <v-dialog
28
+ v-model="showCameraDialog"
29
+ transition="dialog-bottom-transition"
30
+ width="800"
31
+ max-width="800"
32
+ persistent
33
+ @after-enter="startCamera"
34
+ >
35
+ <v-container
36
+ class="d-flex justify-center"
37
+ max-height="90vh"
38
+ width="800"
39
+ max-width="800"
40
+ >
41
+ <v-card elevation="2" class="d-flex flex-column align-center pa-2">
42
+ <v-toolbar>
43
+ <v-card-title class="text-h5">Take a Picture</v-card-title>
44
+ <v-spacer></v-spacer>
45
+ <v-btn
46
+ color="grey-darken-1"
47
+ icon="mdi-close"
48
+ @click="closeCameraDialog"
49
+ ></v-btn>
50
+ </v-toolbar>
51
+
52
+ <div
53
+ id="reader"
54
+ class="d-flex justify-center align-center"
55
+ style="
56
+ position: relative;
57
+ width: 500px;
58
+ min-width: 400px;
59
+ height: 400px;
60
+ "
61
+ >
62
+ <video
63
+ ref="video"
64
+ style="flex: 1; height: 400px; min-width: 300px"
65
+ class="video-shutter"
66
+ autoplay
67
+ ></video>
68
+ <canvas
69
+ ref="canvas"
70
+ style="flex: 1; height: 400px; min-width: 300px; display: none"
71
+ ></canvas>
72
+ </div>
73
+
74
+ <v-row align="center" justify="center">
75
+ <v-col cols="6">
76
+ <v-btn color="primary" icon class="mt-4" @click="switchCamera">
77
+ <v-icon>mdi-camera-switch</v-icon>
78
+ </v-btn>
79
+ </v-col>
80
+ <v-col cols="6">
81
+ <v-btn
82
+ color="secondary"
83
+ icon
84
+ class="mt-4"
85
+ @click="captureImageFromCamera"
86
+ >
87
+ <v-icon large>mdi-camera-outline</v-icon>
88
+ </v-btn>
89
+ </v-col>
90
+ </v-row>
91
+ </v-card>
92
+ </v-container>
93
+ </v-dialog>
94
+ </div>
95
+
96
+ <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
97
+ </template>
98
+
99
+ <script setup lang="ts">
100
+ interface FileWithPreview {
101
+ name: string;
102
+ data: File;
103
+ progress: number;
104
+ url: string;
105
+ }
106
+
107
+ const props = defineProps({
108
+ label: {
109
+ type: String,
110
+ default: "Select File",
111
+ },
112
+ prependIcon: {
113
+ type: String,
114
+ default: "mdi-paperclip",
115
+ },
116
+ required: {
117
+ type: Boolean,
118
+ default: true,
119
+ },
120
+ initFiles: {
121
+ type: Array,
122
+ },
123
+ hasLabel: {
124
+ type: Boolean,
125
+ default: true,
126
+ },
127
+ hasHideInput: {
128
+ type: Boolean,
129
+ default: false,
130
+ },
131
+ });
132
+
133
+ const emit = defineEmits<{
134
+ (event: "onFileAttach", payload: Array<{ data: File }>): void;
135
+ (event: "update:files", files: FileWithPreview[]): void;
136
+ (event: "onFileRemoved", payload: { index: number; file: File }): void;
137
+ (event: "onClear"): void;
138
+ }>();
139
+
140
+ const { showUploadedFiles } = useUploadFiles();
141
+ const isFileUploading = ref<boolean>(false);
142
+
143
+ const files = ref<File[]>([]);
144
+ const showCameraDialog = ref(false);
145
+ const video = ref<HTMLVideoElement | null>(null);
146
+ const canvas = ref<HTMLCanvasElement | null>(null);
147
+ const cameraFacingMode = ref<"environment" | "user">("environment");
148
+
149
+ const message = ref("");
150
+ const messageColor = ref("");
151
+ const messageSnackbar = ref(false);
152
+
153
+ function showMessage(msg: string, color: string) {
154
+ message.value = msg;
155
+ messageColor.value = color;
156
+ messageSnackbar.value = true;
157
+ }
158
+
159
+ watchEffect(() => {
160
+ files.value = [...(props.initFiles as typeof files.value)];
161
+ });
162
+
163
+ const handleFileSelect = async () => {
164
+ if (files.value && files.value.length > 0) {
165
+ const newFiles = files.value.map((file: File) => ({
166
+ name: file.name,
167
+ data: file,
168
+ progress: 0,
169
+ url: URL.createObjectURL(file),
170
+ }));
171
+
172
+ // attachedFiles.value = [...newFiles];
173
+ showUploadedFiles(newFiles);
174
+
175
+ emit("update:files", newFiles);
176
+ } else {
177
+ files.value = [...(props.initFiles as typeof files.value)];
178
+ }
179
+ };
180
+
181
+ const handleClear = () => {
182
+ files.value = [];
183
+ emit("onClear");
184
+ };
185
+
186
+ const openCameraDialog = () => {
187
+ showCameraDialog.value = true;
188
+ };
189
+
190
+ const closeCameraDialog = () => {
191
+ showCameraDialog.value = false;
192
+ stopCamera();
193
+ };
194
+
195
+ const startCamera = async () => {
196
+ try {
197
+ const constraints = {
198
+ video: {
199
+ facingMode: cameraFacingMode.value,
200
+ },
201
+ };
202
+
203
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
204
+ if (video.value) {
205
+ video.value.srcObject = stream;
206
+ video.value.play();
207
+ }
208
+ } catch (error: any) {
209
+ showMessage(error, "error");
210
+ closeCameraDialog();
211
+ }
212
+ };
213
+
214
+ const stopCamera = () => {
215
+ if (video.value) {
216
+ const stream = video.value.srcObject as MediaStream;
217
+ if (stream) {
218
+ stream.getTracks().forEach((track) => track.stop());
219
+ }
220
+ }
221
+ };
222
+
223
+ const switchCamera = async () => {
224
+ await stopCamera();
225
+ cameraFacingMode.value =
226
+ cameraFacingMode.value === "environment" ? "user" : "environment";
227
+ showMessage(
228
+ `Switched to ${
229
+ cameraFacingMode.value === "environment" ? "Back Camera" : "Front Camera"
230
+ }`,
231
+ "error"
232
+ );
233
+ startCamera();
234
+ };
235
+
236
+ const captureImageFromCamera = () => {
237
+ if (!video.value || !canvas.value) return;
238
+
239
+ const context = canvas.value.getContext("2d");
240
+ if (!context) return;
241
+
242
+ // Set canvas dimensions to match video
243
+ canvas.value.width = video.value.videoWidth;
244
+ canvas.value.height = video.value.videoHeight;
245
+
246
+ // Capture the frame
247
+ context.drawImage(video.value, 0, 0, canvas.value.width, canvas.value.height);
248
+
249
+ // Convert to file
250
+ canvas.value.toBlob((blob) => {
251
+ if (!blob) return;
252
+
253
+ const file = new File([blob], `camera-capture-${Date.now()}.png`, {
254
+ type: "image/png",
255
+ });
256
+
257
+ files.value = [file];
258
+ handleFileSelect();
259
+ closeCameraDialog();
260
+ }, "image/png");
261
+ };
262
+
263
+ const removeFile = (index) => {
264
+ const removedFile = files.value[index];
265
+ files.value = files.value.filter((_, i) => i !== index);
266
+ emit("onFileRemoved", { index, file: removedFile }); // Emit when a file is removed
267
+ // emit("onFilesUpdated", files.value); // Emit updated files list
268
+ };
269
+
270
+ // Cleanup
271
+ onUnmounted(() => {
272
+ stopCamera();
273
+ });
274
+ </script>
275
+
276
+ <style scoped>
277
+ .custom-chip {
278
+ max-width: 100%;
279
+ height: auto !important;
280
+ white-space: normal;
281
+ padding: 3px 20px;
282
+ }
283
+
284
+ .chip-text {
285
+ word-break: break-word;
286
+ white-space: normal;
287
+ line-height: 1.2;
288
+ }
289
+ </style>
@@ -22,7 +22,7 @@ const prop = defineProps({
22
22
  },
23
23
  placeholder: {
24
24
  type: String,
25
- default: 'MM/DD/YYYY, HH:MM AM/PM'
25
+ default: 'DD/MM/YYYY, HH:MM AM/PM'
26
26
  }
27
27
  })
28
28
 
@@ -53,17 +53,23 @@ function validate() {
53
53
  function convertToReadableFormat(dateStr: string): string {
54
54
 
55
55
  if (!dateStr) return "";
56
+
56
57
  const date = new Date(dateStr)
57
- const options: Intl.DateTimeFormatOptions = {
58
- year: 'numeric',
59
- month: '2-digit',
60
- day: '2-digit',
61
- hour: '2-digit',
62
- minute: '2-digit',
63
- hour12: true
64
- }
65
- const formatted = date.toLocaleString('en-US', options)
66
- return formatted
58
+
59
+ const day = String(date.getDate()).padStart(2, '0')
60
+ const month = String(date.getMonth() + 1).padStart(2, '0')
61
+ const year = date.getFullYear()
62
+
63
+ let hours = date.getHours()
64
+ const minutes = String(date.getMinutes()).padStart(2, '0')
65
+
66
+ const ampm = hours >= 12 ? 'PM' : 'AM'
67
+ hours = hours % 12
68
+ hours = hours ? hours : 12
69
+
70
+ const formattedTime = `${String(hours).padStart(2, '0')}:${minutes} ${ampm}`
71
+
72
+ return `${day}/${month}/${year}, ${formattedTime}`
67
73
  }
68
74
 
69
75
  function handleInitialDate(){