@7365admin1/layer-common 1.10.8 → 1.10.10

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 (42) 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/BulletinBoardManagement.vue +18 -8
  6. package/components/Carousel.vue +474 -0
  7. package/components/DeliveryCompany.vue +240 -0
  8. package/components/DrawImage.vue +172 -0
  9. package/components/EntryPassInformation.vue +70 -10
  10. package/components/EquipmentItemMain.vue +9 -4
  11. package/components/Feedback/Form.vue +4 -4
  12. package/components/FeedbackMain.vue +734 -146
  13. package/components/FileInput.vue +289 -0
  14. package/components/IncidentReport/Authorities.vue +189 -151
  15. package/components/IncidentReport/IncidentInformation.vue +14 -10
  16. package/components/IncidentReport/IncidentInformationDownload.vue +212 -0
  17. package/components/IncidentReport/affectedEntities.vue +8 -57
  18. package/components/SiteSettings.vue +285 -0
  19. package/components/StockCard.vue +11 -7
  20. package/components/Tooltip/Info.vue +33 -0
  21. package/components/VisitorForm.vue +176 -45
  22. package/components/VisitorManagement.vue +23 -6
  23. package/composables/useAccessManagement.ts +60 -18
  24. package/composables/useBulletin.ts +8 -3
  25. package/composables/useBulletinBoardPermission.ts +48 -0
  26. package/composables/useCleaningPermission.ts +2 -0
  27. package/composables/useCommonPermission.ts +29 -1
  28. package/composables/useEquipmentManagement.ts +63 -0
  29. package/composables/useFeedback.ts +53 -21
  30. package/composables/useFile.ts +6 -0
  31. package/composables/useLocalAuth.ts +29 -1
  32. package/composables/useSiteSettings.ts +1 -1
  33. package/composables/useUploadFiles.ts +94 -0
  34. package/composables/useUtils.ts +152 -53
  35. package/composables/useVisitor.ts +9 -6
  36. package/constants/app.ts +12 -0
  37. package/nuxt.config.ts +2 -0
  38. package/package.json +3 -1
  39. package/plugins/vue-draggable-next.client.ts +5 -0
  40. package/types/feedback.d.ts +5 -2
  41. package/types/site.d.ts +2 -1
  42. package/types/user.d.ts +1 -0
@@ -0,0 +1,240 @@
1
+ <template>
2
+ <v-row no-gutters class="pa-3">
3
+ <v-col cols="12">
4
+ <v-card width="100%" variant="outlined" border="thin" rounded="lg" max-width="700" max-height="500"
5
+ style="overflow-y: auto;">
6
+ <v-toolbar density="compact" color="grey-lighten-4 pl-2 pr-4">
7
+ <v-row v-if="!hideTitle" no-gutters class="px-3 font-weight-bold">
8
+ Delivery Companies
9
+ </v-row>
10
+
11
+ <template #append>
12
+ <v-btn v-if="!props.readOnly" variant="flat" color="primary" class="text-none"
13
+ @click="handleAdd">
14
+ Add
15
+ </v-btn>
16
+ </template>
17
+ </v-toolbar>
18
+
19
+ <v-card-text class="pa-0" style="min-height: 100px;">
20
+ <template v-if="companies.length > 0">
21
+ <draggable :list="companies" item-key="index" class="drag-area"
22
+ :disabled="editingIndex !== null">
23
+ <v-list-item v-for="(item, index) in companies" :key="index" class="mt-1">
24
+ <v-row class="d-flex justify-space-between align-center" no-gutters>
25
+ <span class="d-flex align-center">
26
+ <v-btn icon="mdi-drag" class="drag-handle text-grey mr-2" flat
27
+ density="compact" />
28
+
29
+ <div v-if="editingIndex === index" class="w-full">
30
+ <v-text-field v-model="editedValue" class="text-subtitle-2"
31
+ prepend-icon="mdi-truck" style="min-width: 300px;" density="compact"
32
+ hide-details autofocus />
33
+ </div>
34
+
35
+ <div v-else>
36
+ <span>
37
+ <v-icon icon="mdi-truck" class="text-h6 mr-2" />
38
+ </span>
39
+ {{ item }}
40
+ </div>
41
+ </span>
42
+
43
+ <span class="d-flex align-center ga-2">
44
+ <template v-if="editingIndex === index">
45
+ <v-btn icon="mdi-check" color="green" density="compact"
46
+ @click="saveEdit(index)" />
47
+ <v-btn icon="mdi-close" color="grey" density="compact"
48
+ @click="cancelEdit" />
49
+ </template>
50
+
51
+ <template v-else>
52
+ <v-btn v-if="!props.readOnly" flat icon="mdi-pencil" class="text-grey"
53
+ density="compact" @click="startEdit(index)" />
54
+ <v-btn v-if="!props.readOnly" flat icon="mdi-trash-outline" class="text-red"
55
+ density="compact" @click="handleRemove(index)" />
56
+ </template>
57
+ </span>
58
+ </v-row>
59
+ </v-list-item>
60
+ </draggable>
61
+ </template>
62
+
63
+ <div v-else class="text-center py-5">
64
+ No delivery companies added yet.
65
+ </div>
66
+ </v-card-text>
67
+ </v-card>
68
+ </v-col>
69
+
70
+ <v-dialog v-model="dialog.add" persistent width="450">
71
+ <v-card width="100%">
72
+ <v-toolbar>
73
+ <v-row no-gutters class="fill-height px-6" align="center">
74
+ <span class="font-weight-bold text-h5 text-capitalize">
75
+ Add Delivery Company
76
+ </span>
77
+ </v-row>
78
+ </v-toolbar>
79
+
80
+ <v-card-text>
81
+ <v-text-field v-model="editedValue" placeholder="Company Name" autofocus />
82
+ </v-card-text>
83
+
84
+ <v-toolbar>
85
+ <v-row no-gutters justify="end" align="center" class="px-5">
86
+ <v-btn variant="text" text="Cancel" @click="dialog.add = false" />
87
+ <v-btn variant="flat" color="primary" class="text-none ml-2"
88
+ @click="saveEdit(companies.length)">
89
+ Add
90
+ </v-btn>
91
+ </v-row>
92
+ </v-toolbar>
93
+ </v-card>
94
+ </v-dialog>
95
+
96
+ <Snackbar v-model="toast.show" :text="toast.message" :color="toast.color" />
97
+
98
+ <v-dialog v-model="dialog.delete" persistent width="450">
99
+ <CardDeleteConfirmation
100
+ :prompt-title="`Are you sure you want to delete this ${companies?.[deleteIndex!]}?`"
101
+ @close="dialog.delete = false"
102
+ @delete="removeCompany(deleteIndex!)"
103
+ />
104
+ </v-dialog>
105
+ </v-row>
106
+ </template>
107
+
108
+ <script setup lang="ts">
109
+ import useSiteSettings from '../composables/useSiteSettings'
110
+
111
+ const props = defineProps<{
112
+ site: string
113
+ readOnly?: boolean
114
+ hideTitle?: boolean
115
+ }>()
116
+
117
+ const emit = defineEmits<{
118
+ (e: 'update:companiesValue', value: string[]): void
119
+ (e: 'refresh-site'): void
120
+ }>()
121
+
122
+ const { updateSitebyId } = useSiteSettings()
123
+
124
+ const initialCompanies = defineModel<string[]>('initial', {
125
+ type: Array,
126
+ default: []
127
+ })
128
+
129
+ const dialog = reactive({
130
+ add: false,
131
+ delete: false,
132
+ })
133
+
134
+ const toast = reactive({
135
+ show: false,
136
+ message: '',
137
+ color: 'success',
138
+ })
139
+
140
+ const companies = ref<string[]>([])
141
+ const editingIndex = ref<number | null>(null)
142
+ const editedValue = ref('')
143
+ const isHydrating = ref(true)
144
+ const deleteIndex = ref<number | null>(null)
145
+
146
+
147
+
148
+ watch(
149
+ companies,
150
+ async (newVal) => {
151
+ await persistCompanies()
152
+ },
153
+ { deep: true, immediate: false }
154
+ )
155
+
156
+ const startEdit = (index: number) => {
157
+ editingIndex.value = index
158
+ editedValue.value = companies.value[index] || ''
159
+ }
160
+
161
+ const saveEdit = async (index: number) => {
162
+ const value = editedValue.value.trim()
163
+ if (!value) return
164
+
165
+ const updated = [...companies.value]
166
+
167
+ if (index >= updated.length) {
168
+ updated.push(value)
169
+ } else {
170
+ updated[index] = value
171
+ }
172
+
173
+ companies.value = updated
174
+ editingIndex.value = null
175
+ editedValue.value = ''
176
+ dialog.add = false
177
+ }
178
+
179
+ const cancelEdit = () => {
180
+ editingIndex.value = null
181
+ editedValue.value = ''
182
+ }
183
+
184
+ const handleAdd = () => {
185
+ dialog.add = true
186
+ editedValue.value = ''
187
+ editingIndex.value = null
188
+ }
189
+
190
+ const removeCompany = async (index: number) => {
191
+ const updated = [...companies.value]
192
+ updated.splice(index, 1)
193
+ companies.value = updated
194
+ dialog.delete = false
195
+ }
196
+
197
+ const handleRemove = async (index: number) => {
198
+ deleteIndex.value = index;
199
+ dialog.delete = true;
200
+ }
201
+
202
+ const persistCompanies = async () => {
203
+ try {
204
+ const isSame =
205
+ companies.value.length === initialCompanies.value.length &&
206
+ companies.value.every((c, i) => c === initialCompanies.value[i]);
207
+ if (isSame) return
208
+
209
+
210
+ await updateSitebyId(props.site, {
211
+ deliveryCompanyList: companies.value,
212
+ })
213
+
214
+ toast.message = 'Delivery companies updated successfully!'
215
+ toast.color = 'success'
216
+ toast.show = true
217
+ emit('refresh-site')
218
+ } catch (error) {
219
+ console.error('Failed to update delivery companies:', error)
220
+ toast.message = 'Failed to update delivery companies. Please try again.'
221
+ toast.color = 'error'
222
+ toast.show = true
223
+ }
224
+ }
225
+
226
+ watch(initialCompanies, (val) => {
227
+ companies.value = Array.isArray(val) ? [...val] : []
228
+ isHydrating.value = false
229
+ }, { immediate: true })
230
+ </script>
231
+
232
+ <style scoped>
233
+ .drag-area {
234
+ cursor: grab;
235
+ }
236
+
237
+ .drag-handle {
238
+ cursor: grab;
239
+ }
240
+ </style>
@@ -0,0 +1,172 @@
1
+ <template>
2
+ <v-dialog
3
+ v-model="isDialogVisible"
4
+ transition="dialog-top-transition"
5
+ max-width="1000"
6
+ >
7
+ <v-card class="pa-1">
8
+ <v-toolbar>
9
+ <v-spacer></v-spacer>
10
+ <v-btn icon="mdi-close" @click="emit('onCloseDialog')" />
11
+ </v-toolbar>
12
+ <canvas id="imageCanvas" />
13
+ <v-card-title class="text-end">
14
+ <v-btn
15
+ color="orange-darken-3"
16
+ size="small"
17
+ variant="flat"
18
+ style="height: 40px; margin-right: 12px"
19
+ @click="onClearDrawing()"
20
+ >
21
+ Clear Drawing
22
+ </v-btn>
23
+ <v-btn
24
+ color="blue-darken-3"
25
+ size="small"
26
+ variant="flat"
27
+ style="height: 40px"
28
+ @click="submit"
29
+ >
30
+ Submit
31
+ </v-btn>
32
+ </v-card-title>
33
+ </v-card>
34
+ </v-dialog>
35
+ </template>
36
+
37
+ <script setup lang="ts">
38
+ const { getImage } = useUtils();
39
+
40
+ const props = defineProps({
41
+ message: {
42
+ type: String,
43
+ },
44
+ isShowDialog: {
45
+ type: Boolean,
46
+ default: false,
47
+ },
48
+ imageUrl: {
49
+ type: String,
50
+ },
51
+ imageIdx: {
52
+ type: Number,
53
+ },
54
+ });
55
+ const emit = defineEmits<{
56
+ (event: "onSubmit", value: string, idx: number): void;
57
+ (event: "onCloseDialog"): void;
58
+ }>();
59
+
60
+ const isDialogVisible = computed(() => props.isShowDialog);
61
+
62
+ var canvas: any = document.getElementById("imageCanvas");
63
+ var ctx: any = "";
64
+ var img: any = "";
65
+ var pos: any = { x: 0, y: 0 };
66
+
67
+ onMounted(async () => {
68
+ canvas = document.getElementById("imageCanvas");
69
+ ctx = canvas?.getContext("2d");
70
+
71
+ var rect = canvas.getBoundingClientRect();
72
+ canvas.width = rect.width < 712 ? 712 : rect.width;
73
+ canvas.height = rect.height < 400 ? 400 : rect.height;
74
+
75
+ const response = await getImage(props.imageUrl!);
76
+ if (!response) return;
77
+ handleImage(response);
78
+
79
+ canvas.addEventListener("mousedown", setPosition);
80
+ canvas.addEventListener("mousemove", draw);
81
+ canvas.addEventListener(
82
+ "touchstart",
83
+ function (e: any) {
84
+ var touch = e.touches[0];
85
+ var mouseEvent = new MouseEvent("mousedown", {
86
+ buttons: 1,
87
+ clientX: touch.clientX,
88
+ clientY: touch.clientY,
89
+ });
90
+ canvas.dispatchEvent(mouseEvent);
91
+ },
92
+ false,
93
+ );
94
+ canvas.addEventListener(
95
+ "touchmove",
96
+ function (e: any) {
97
+ var touch = e.touches[0];
98
+ var mouseEvent = new MouseEvent("mousemove", {
99
+ buttons: 1,
100
+ clientX: touch.clientX,
101
+ clientY: touch.clientY,
102
+ });
103
+ canvas.dispatchEvent(mouseEvent);
104
+ },
105
+ false,
106
+ );
107
+
108
+ var imageCanvas: any = document.getElementById("imageCanvas");
109
+ imageCanvas.style.cursor = "crosshair";
110
+ });
111
+
112
+ function handleImage(image: Blob) {
113
+ var reader = new FileReader();
114
+ reader.onload = function (event: any) {
115
+ img = new Image();
116
+ img.onload = function () {
117
+ var ratio = this.height / this.width;
118
+ canvas.height = canvas.width * ratio;
119
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
120
+ };
121
+ img.src = event.target.result;
122
+ };
123
+ reader.readAsDataURL(image);
124
+ }
125
+
126
+ async function setPosition(e: any) {
127
+ var rect = canvas.getBoundingClientRect();
128
+
129
+ if (rect.width < 712 && rect.height < 400) {
130
+ var scaleX = 712 / rect.width;
131
+ var scaleY = 400 / rect.height;
132
+
133
+ pos.x = (e.clientX - rect.left) * scaleX;
134
+ pos.y = (e.clientY - rect.top) * scaleY;
135
+ return;
136
+ }
137
+
138
+ pos.x = e.clientX - rect.left;
139
+ pos.y = e.clientY - rect.top;
140
+ }
141
+
142
+ async function draw(e: any) {
143
+ canvas.style.cursor = "crosshair";
144
+
145
+ if (e.buttons !== 1) return;
146
+ if (pos.x === 0 && pos.y === 0) setPosition(e);
147
+
148
+ ctx.beginPath();
149
+ ctx.lineWidth = 5;
150
+ ctx.lineCap = "round";
151
+ ctx.strokeStyle = "#FF0000";
152
+ ctx.moveTo(pos.x, pos.y);
153
+ setPosition(e);
154
+ ctx.lineTo(pos.x, pos.y);
155
+ ctx.stroke();
156
+ ctx.closePath();
157
+ }
158
+
159
+ const submit = () => {
160
+ canvas.toBlob((blob: any) => {
161
+ const url = URL.createObjectURL(blob);
162
+ console.log("URL", url);
163
+
164
+ emit("onSubmit", url, props.imageIdx!);
165
+ });
166
+ };
167
+
168
+ async function onClearDrawing() {
169
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
170
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
171
+ }
172
+ </script>
@@ -46,12 +46,19 @@
46
46
  No printer is configured in the settings. QR Pass may not print.
47
47
  </v-alert>
48
48
  <template v-else>
49
- <InputLabel title="Quantity" class="mt-3" />
49
+ <div class="d-flex align-center justify-space-between mt-3">
50
+ <InputLabel title="Quantity" />
51
+ <span v-if="availableCount !== null" class="text-caption text-grey">
52
+ {{ availableCount }} available
53
+ </span>
54
+ <v-progress-circular v-else-if="cardsLoading" size="14" width="2" indeterminate color="grey" />
55
+ </div>
50
56
  <v-text-field
51
57
  v-model.number="quantity"
52
58
  type="number"
53
59
  density="comfortable"
54
60
  :min="1"
61
+ :max="availableCount ?? undefined"
55
62
  hide-details
56
63
  />
57
64
  </template>
@@ -65,7 +72,7 @@
65
72
  v-model="selectedCards"
66
73
  :items="cardItems"
67
74
  :loading="cardsLoading"
68
- item-title="cardNumber"
75
+ item-title="cardNo"
69
76
  item-value="_id"
70
77
  density="comfortable"
71
78
  multiple
@@ -78,7 +85,7 @@
78
85
  <template #item="{ props: itemProps, item }">
79
86
  <v-list-item v-bind="itemProps">
80
87
  <template #subtitle>
81
- <span class="text-caption text-grey">{{ item.raw.cardNumber }}</span>
88
+ <span class="text-caption text-grey">{{ item.raw.cardNo }}</span>
82
89
  </template>
83
90
  </v-list-item>
84
91
  </template>
@@ -182,6 +189,8 @@
182
189
  </v-card>
183
190
  </v-container>
184
191
  </v-dialog>
192
+
193
+ <Snackbar v-model="snackbar" :text="snackbarText" :color="snackbarColor" />
185
194
  </div>
186
195
  </template>
187
196
 
@@ -207,11 +216,19 @@ const props = defineProps({
207
216
  type: Array as PropType<any[]>,
208
217
  default: () => [],
209
218
  },
219
+ siteId: {
220
+ type: String as PropType<string | null>,
221
+ default: null,
222
+ },
223
+ unitId: {
224
+ type: String as PropType<string | null>,
225
+ default: null,
226
+ },
210
227
  });
211
228
 
212
229
  const emit = defineEmits(["update:modelValue", "update:quantity", "update:cards"]);
213
230
 
214
- const { getAll: getAllCards } = useCard();
231
+ const { getAvailableContractorCards, generateQrVms } = useAccessManagement();
215
232
 
216
233
  const nfcEnabled = computed(() => props.settings?.data?.settings?.nfcPass ?? false);
217
234
  const printer = computed(() => props.settings?.data?.settings?.printer ?? { vendorId: null, productId: null });
@@ -235,15 +252,54 @@ const selectedCards = computed({
235
252
  },
236
253
  });
237
254
 
255
+ // ─── Snackbar ─────────────────────────────────────────────────────
256
+ const snackbar = ref(false);
257
+ const snackbarText = ref("");
258
+ const snackbarColor = ref("error");
259
+
260
+ function showSnackbar(text: string, color = "error") {
261
+ snackbarText.value = text;
262
+ snackbarColor.value = color;
263
+ snackbar.value = true;
264
+ }
265
+
238
266
  // ─── Card fetching ────────────────────────────────────────────────
239
267
  const cardItems = ref<any[]>([]);
240
268
  const cardsLoading = ref(false);
269
+ const availableCount = ref<number | null>(null);
241
270
 
242
- async function fetchCards() {
271
+ async function fetchCards(type: "NFC" | "QRCODE" = "NFC") {
272
+ if (!props.siteId || !props.unitId) return;
243
273
  cardsLoading.value = true;
244
274
  try {
245
- const res = await getAllCards({ assignFilter: "available", limit: 100 });
246
- cardItems.value = res?.data ?? [];
275
+ const res = await getAvailableContractorCards({
276
+ type,
277
+ siteId: props.siteId,
278
+ unitId: props.unitId,
279
+ });
280
+ cardItems.value = res?.data?.[0]?.items ?? [];
281
+ availableCount.value = (res?.data?.[2] as any)?.count ?? cardItems.value.length;
282
+
283
+ if (type === "QRCODE" && props.settings?.data?._id && availableCount.value !== null && availableCount.value < 50) {
284
+ await generateQrVms({
285
+ site: props.settings.data.site,
286
+ unitId: props.unitId,
287
+ quantity: 50,
288
+ });
289
+ // Re-fetch to get the updated count after generation
290
+ const refreshed = await getAvailableContractorCards({
291
+ type,
292
+ siteId: props.siteId,
293
+ unitId: props.unitId,
294
+ });
295
+ cardItems.value = refreshed?.data?.[0]?.items ?? [];
296
+ availableCount.value = (refreshed?.data?.[2] as any)?.count ?? cardItems.value.length;
297
+ }
298
+ } catch (err: any) {
299
+ const msg = err?.data?.message ?? err?.message ?? String(err);
300
+ showSnackbar(`Entry pass service error: ${msg}`);
301
+ availableCount.value = null;
302
+ cardItems.value = [];
247
303
  } finally {
248
304
  cardsLoading.value = false;
249
305
  }
@@ -268,7 +324,7 @@ const toggleBarcodeScanning = () => {
268
324
  };
269
325
 
270
326
  const validateScannedBarcode = (scannedCode: string) => {
271
- const card = cardItems.value.find((c) => c.cardNumber === scannedCode);
327
+ const card = cardItems.value.find((c) => c.cardNo === scannedCode);
272
328
  if (card) addCard(card);
273
329
  barcodeBuffer.value = "";
274
330
  };
@@ -360,7 +416,7 @@ const scanFrame = async () => {
360
416
  try {
361
417
  const barcodes = await barcodeDetector.detect(canvas);
362
418
  for (const barcode of barcodes) {
363
- const card = cardItems.value.find((c) => c.cardNumber === barcode.rawValue);
419
+ const card = cardItems.value.find((c) => c.cardNo === barcode.rawValue);
364
420
  if (card) {
365
421
  addCard(card);
366
422
  closeCameraDialog();
@@ -390,9 +446,13 @@ watch(
390
446
  () => passType.value,
391
447
  (val) => {
392
448
  if (val === "NFC") {
393
- fetchCards();
449
+ fetchCards("NFC");
394
450
  window.addEventListener("keydown", handleBarcodeInput);
451
+ } else if (val === "QR") {
452
+ availableCount.value = null;
453
+ fetchCards("QRCODE");
395
454
  } else {
455
+ availableCount.value = null;
396
456
  isBarcodeScanningActive.value = false;
397
457
  barcodeBuffer.value = "";
398
458
  if (barcodeTimeout.value) clearTimeout(barcodeTimeout.value);
@@ -283,7 +283,9 @@
283
283
  >mdi-delete-outline</v-icon
284
284
  >
285
285
  </template>
286
- <v-list-item-title>Remove Equipment</v-list-item-title>
286
+ <v-list-item-title
287
+ >Remove Equipment</v-list-item-title
288
+ >
287
289
  </v-list-item>
288
290
  </v-list>
289
291
  </v-menu>
@@ -440,7 +442,8 @@
440
442
  <script setup lang="ts">
441
443
  import useEquipmentItem from "../composables/useEquipmentItem";
442
444
  import { useEquipmentItemPermission } from "../composables/useEquipmentItemPermission";
443
- import useSupply from "../composables/useSupply";
445
+ import useEquipmentManagement from "../composables/useEquipmentManagement";
446
+ import useFile from "../composables/useFile";
444
447
  import useUtils from "../composables/useUtils";
445
448
 
446
449
  const props = defineProps({
@@ -546,7 +549,7 @@ watchEffect(() => {
546
549
  }
547
550
  });
548
551
 
549
- const { getSupplies } = useSupply();
552
+ const { getSupplies } = useEquipmentManagement();
550
553
 
551
554
  const { data: getSuppliesReq } = await useLazyAsyncData(
552
555
  "get-supplies-for-equipment-item",
@@ -568,7 +571,9 @@ watchEffect(() => {
568
571
  }
569
572
  });
570
573
 
571
- const supplyItems = ref<Array<{ name: string; value: string; stockQty: number }>>([]);
574
+ const supplyItems = ref<
575
+ Array<{ name: string; value: string; stockQty: number }>
576
+ >([]);
572
577
  const selectedSupplyForAdd = ref<string>("");
573
578
  const equipmentItems = ref<TEquipmentItem[]>([]);
574
579
 
@@ -31,7 +31,7 @@
31
31
  <template #label> Subject <span class="text-red">*</span> </template>
32
32
  </v-autocomplete>
33
33
 
34
- <v-autocomplete
34
+ <!-- <v-autocomplete
35
35
  v-model="localFeedback.category"
36
36
  :items="categories"
37
37
  item-title="title"
@@ -45,7 +45,7 @@
45
45
  :rules="[requiredRule]"
46
46
  >
47
47
  <template #label> Category <span class="text-red">*</span> </template>
48
- </v-autocomplete>
48
+ </v-autocomplete> -->
49
49
 
50
50
  <v-text-field
51
51
  label="Location"
@@ -91,7 +91,7 @@ const props = defineProps<{
91
91
  feedback: TFeedbackCreate;
92
92
  isEditMode: boolean;
93
93
  loading: boolean;
94
- categories: { title: string; value: string }[];
94
+ // categories: { title: string; value: string }[];
95
95
  erroredImages?: string[];
96
96
  maxFiles?: number;
97
97
  }>();
@@ -133,7 +133,7 @@ const { requiredRule } = useUtils();
133
133
  const isFormValid = computed(() => {
134
134
  return (
135
135
  localFeedback.value.subject &&
136
- localFeedback.value.category &&
136
+ // localFeedback.value.category &&
137
137
  localFeedback.value.location &&
138
138
  localFeedback.value.description
139
139
  );