@iservice365/layer-common 1.5.7 → 1.7.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.
@@ -0,0 +1,414 @@
1
+ <template>
2
+ <v-row no-gutters>
3
+ <v-col cols="12" class="mb-2">
4
+ <v-row no-gutters align="center" justify="space-between">
5
+ <v-btn
6
+ class="text-none"
7
+ rounded="pill"
8
+ variant="tonal"
9
+ size="large"
10
+ @click="setCard()"
11
+ v-if="canCreate && canCreateAccessCard"
12
+ >
13
+ Add Access Card
14
+ </v-btn>
15
+
16
+ <v-text-field
17
+ v-model="searchText"
18
+ placeholder="Search Card, Unit..."
19
+ variant="outlined"
20
+ density="comfortable"
21
+ clearable
22
+ hide-details
23
+ class="ml-2"
24
+ style="max-width: 250px"
25
+ />
26
+ </v-row>
27
+ </v-col>
28
+ <v-col cols="12">
29
+ <v-card
30
+ width="100%"
31
+ variant="outlined"
32
+ border="thin"
33
+ rounded="lg"
34
+ :loading="loading"
35
+ >
36
+ <v-toolbar density="compact" color="grey-lighten-4">
37
+ <template #prepend>
38
+ <v-btn fab icon density="comfortable" @click="getCards">
39
+ <v-icon>mdi-refresh</v-icon>
40
+ </v-btn>
41
+ </template>
42
+
43
+ <template #append>
44
+ <v-row no-gutters justify="end" align="center">
45
+ <span class="mr-2 text-caption text-fontgray">
46
+ {{ pageRange }}
47
+ </span>
48
+ <local-pagination
49
+ v-model="page"
50
+ :length="pages"
51
+ @update:value="getCards"
52
+ />
53
+ </v-row>
54
+ </template>
55
+ </v-toolbar>
56
+ <v-data-table
57
+ :headers="headers"
58
+ :items="items"
59
+ item-value="_id"
60
+ items-per-page="10"
61
+ fixed-header
62
+ hide-default-footer
63
+ @click:row="tableRowClickHandler"
64
+ style="max-height: calc(100vh - (200px))"
65
+ />
66
+ </v-card>
67
+ </v-col>
68
+
69
+ <!-- Create Dialog -->
70
+ <v-dialog v-model="createDialog" width="650" persistent>
71
+ <AccessCardAddForm
72
+ @cancel="createDialog = false"
73
+ @success="successCreate()"
74
+ />
75
+ </v-dialog>
76
+
77
+ <!-- Edit Dialog -->
78
+ <v-dialog v-model="editDialog" width="650" persistent>
79
+ <AccessCardAddForm
80
+ mode="edit"
81
+ @cancel="editDialog = false"
82
+ @success="successUpdate()"
83
+ :card="selectedCard"
84
+ />
85
+ </v-dialog>
86
+
87
+ <!-- Preview Dialog -->
88
+ <v-dialog v-model="previewDialog" width="450" persistent>
89
+ <v-card width="100%">
90
+ <v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
91
+ <v-row no-gutters class="mb-4">
92
+ <v-col cols="12">
93
+ <strong>Card:</strong> {{ selectedCard?.cardNumber ?? "N/A" }}
94
+ </v-col>
95
+ <v-col cols="12">
96
+ <strong>Access Type:</strong>
97
+ {{ selectedCard?.accessCardType ?? "N/A" }}
98
+ </v-col>
99
+ <!-- <v-col cols="12">
100
+ <strong>Visitor Type:</strong>
101
+ {{ selectedCard?.visitorType ?? "N/A" }}
102
+ </v-col> -->
103
+ <v-col cols="12">
104
+ <strong>Unit:</strong>
105
+ {{ selectedCard?.unit || "N/A" }}
106
+ </v-col>
107
+ <v-col cols="12">
108
+ <strong>Assign:</strong> {{ selectedCard?.assign || "N/A" }}
109
+ </v-col>
110
+ <v-col cols="12">
111
+ <strong>Status:</strong> {{ selectedCard?.status ?? "N/A" }}
112
+ </v-col>
113
+ </v-row></v-card-text
114
+ >
115
+ <v-toolbar class="pa-0" density="compact">
116
+ <v-row no-gutters>
117
+ <v-col cols="6" class="pa-0">
118
+ <v-btn
119
+ block
120
+ variant="text"
121
+ class="text-none"
122
+ size="large"
123
+ @click="previewDialog = false"
124
+ height="48"
125
+ >
126
+ Close
127
+ </v-btn>
128
+ </v-col>
129
+ <v-col cols="6" class="pa-0" v-if="canUpdate">
130
+ <v-menu>
131
+ <template #activator="{ props }">
132
+ <v-btn
133
+ block
134
+ variant="flat"
135
+ color="black"
136
+ class="text-none"
137
+ height="48"
138
+ v-bind="props"
139
+ tile
140
+ :disabled="!canUpdateAccessCard && !canDeleteAccessCard"
141
+ >
142
+ More actions
143
+ </v-btn>
144
+ </template>
145
+ <v-list class="pa-0">
146
+ <v-list-item @click="openEditDialog()">
147
+ <v-list-item-title class="text-subtitle-2">
148
+ Edit Card
149
+ </v-list-item-title>
150
+ </v-list-item>
151
+ <v-list-item @click="openReplaceDialog()">
152
+ <v-list-item-title class="text-subtitle-2">
153
+ Replace Card
154
+ </v-list-item-title>
155
+ </v-list-item>
156
+ <v-list-item @click="openDeleteDialog()" class="text-red">
157
+ <v-list-item-title class="text-subtitle-2">
158
+ Delete Card
159
+ </v-list-item-title>
160
+ </v-list-item>
161
+ </v-list>
162
+ </v-menu>
163
+ </v-col>
164
+ </v-row>
165
+ </v-toolbar></v-card
166
+ >
167
+ </v-dialog>
168
+
169
+ <!-- Delete Dialog -->
170
+ <v-dialog
171
+ v-model="confirmDialog"
172
+ :loading="deleteLoading"
173
+ width="450"
174
+ persistent
175
+ >
176
+ <v-card width="100%">
177
+ <v-toolbar density="compact" class="pl-4">
178
+ <span class="font-weight-medium text-h5">Delete Card</span>
179
+ </v-toolbar>
180
+ <v-card-text>
181
+ <p class="text-subtitle-2 text-center">
182
+ Are you sure you want to delete this card? This action cannot be
183
+ undone.
184
+ </p>
185
+
186
+ <v-row v-if="message" no-gutters justify="center" class="mt-4">
187
+ <span class="text-caption text-error text-center">
188
+ {{ message }}
189
+ </span>
190
+ </v-row></v-card-text
191
+ >
192
+ <v-toolbar density="compact">
193
+ <v-row no-gutters>
194
+ <v-col cols="6">
195
+ <v-btn
196
+ tile
197
+ block
198
+ size="48"
199
+ variant="text"
200
+ class="text-none"
201
+ @click="confirmDialog = false"
202
+ :disabled="deleteLoading"
203
+ >
204
+ Close
205
+ </v-btn>
206
+ </v-col>
207
+ <v-col cols="6">
208
+ <v-btn
209
+ tile
210
+ block
211
+ size="48"
212
+ color="black"
213
+ variant="flat"
214
+ class="text-none"
215
+ @click="handleDeleteCard"
216
+ :loading="deleteLoading"
217
+ >
218
+ Delete Card
219
+ </v-btn>
220
+ </v-col>
221
+ </v-row></v-toolbar
222
+ >
223
+ </v-card>
224
+ </v-dialog>
225
+
226
+ <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
227
+ </v-row>
228
+ </template>
229
+ <script setup lang="ts">
230
+ definePageMeta({
231
+ middleware: ["01-auth", "02-org"],
232
+ memberOnly: true,
233
+ });
234
+ const props = defineProps({
235
+ headers: {
236
+ type: Array as PropType<Array<Record<string, any>>>,
237
+ default: () => [
238
+ {
239
+ title: "Card",
240
+ value: "cardNumber",
241
+ },
242
+ {
243
+ title: "Unit",
244
+ value: "unit",
245
+ },
246
+ {
247
+ title: "Assign",
248
+ value: "assign",
249
+ },
250
+ { title: "Action", value: "action-table" },
251
+ ],
252
+ },
253
+ canCreate: {
254
+ type: Boolean,
255
+ default: true,
256
+ },
257
+ canUpdate: {
258
+ type: Boolean,
259
+ default: true,
260
+ },
261
+ canDelete: {
262
+ type: Boolean,
263
+ default: true,
264
+ },
265
+ canCreateAccessCard: {
266
+ type: Boolean,
267
+ default: true,
268
+ },
269
+ canUpdateAccessCard: {
270
+ type: Boolean,
271
+ default: true,
272
+ },
273
+ canDeleteAccessCard: {
274
+ type: Boolean,
275
+ default: true,
276
+ },
277
+ });
278
+
279
+ // const { headerSearch } = useLocal();
280
+ const page = ref(1);
281
+ const pages = ref(0);
282
+ const pageRange = ref("-- - -- of --");
283
+
284
+ const message = ref("");
285
+ const messageSnackbar = ref(false);
286
+ const messageColor = ref("");
287
+
288
+ const items = ref<Array<Record<string, any>>>([]);
289
+ const createDialog = ref(false);
290
+ const editDialog = ref(false);
291
+ const previewDialog = ref(false);
292
+ const deleteLoading = ref(false);
293
+ const confirmDialog = ref(false);
294
+ const searchText = ref("");
295
+ const replaceDialog = ref(false);
296
+
297
+ const selectedCard = ref<TCard>({
298
+ _id: "",
299
+ name: "",
300
+ accessCardType: "",
301
+ // visitorType: "",
302
+ type: "",
303
+ cardNumber: "",
304
+ startDate: "",
305
+ endDate: "",
306
+ door: "",
307
+ accessGroup: [],
308
+ cardType: "",
309
+ pinNo: "",
310
+ useAsLiftCard: false,
311
+ liftAccessLevel: "",
312
+ isActivate: true,
313
+ isAntiPassBack: false,
314
+ status: "",
315
+ org: "",
316
+ site: "",
317
+ unit: "",
318
+ assign: "",
319
+ });
320
+ const selectedCardId = ref<string | null>(null);
321
+
322
+ const { getAll: _getAllCards, deleteById: _deleteCard } = useCard();
323
+
324
+ const {
325
+ data: getCardReq,
326
+ refresh: getCards,
327
+ status: getAllReqStatus,
328
+ } = useLazyAsyncData(
329
+ "get-all-cards",
330
+ () =>
331
+ _getAllCards({
332
+ page: page.value,
333
+ search: searchText.value,
334
+ // search: headerSearch.value,
335
+ }),
336
+ {
337
+ watch: [page, searchText],
338
+ }
339
+ );
340
+
341
+ const loading = computed(() => getAllReqStatus.value === "pending");
342
+
343
+ watchEffect(() => {
344
+ if (getCardReq.value) {
345
+ items.value = getCardReq.value.items;
346
+ pages.value = getCardReq.value.pages;
347
+ pageRange.value = getCardReq.value.pageRange;
348
+ }
349
+ });
350
+
351
+ function setCard({ mode = "create", dialog = true, data = {} as TCard } = {}) {
352
+ if (mode === "create") {
353
+ createDialog.value = dialog;
354
+ } else if (mode === "edit") {
355
+ editDialog.value = dialog;
356
+ selectedCard.value = data;
357
+ } else if (mode === "preview") {
358
+ previewDialog.value = dialog;
359
+ selectedCard.value = data;
360
+ }
361
+ }
362
+
363
+ function successCreate() {
364
+ createDialog.value = false;
365
+ getCards();
366
+ showMessage("Card created successfully!", "success");
367
+ }
368
+
369
+ function successUpdate() {
370
+ editDialog.value = false;
371
+ previewDialog.value = false;
372
+ getCards();
373
+ showMessage("Card updated successfully!", "success");
374
+ }
375
+
376
+ function showMessage(msg: string, color: string) {
377
+ message.value = msg;
378
+ messageColor.value = color;
379
+ messageSnackbar.value = true;
380
+ }
381
+
382
+ function tableRowClickHandler(_: any, data: any) {
383
+ selectedCard.value = data.item as TCard;
384
+ previewDialog.value = true;
385
+ }
386
+
387
+ function openEditDialog() {
388
+ editDialog.value = true;
389
+ }
390
+
391
+ function openDeleteDialog() {
392
+ confirmDialog.value = true;
393
+ message.value = "";
394
+ }
395
+
396
+ function openReplaceDialog() {
397
+ replaceDialog.value = true;
398
+ }
399
+
400
+ async function handleDeleteCard() {
401
+ deleteLoading.value = true;
402
+ try {
403
+ await _deleteCard(selectedCard.value._id ?? "");
404
+ await getCards();
405
+ selectedCardId.value = null;
406
+ confirmDialog.value = false;
407
+ previewDialog.value = false;
408
+ } catch (error: any) {
409
+ message.value = error?.response?._data?.message || "Failed to delete card";
410
+ } finally {
411
+ deleteLoading.value = false;
412
+ }
413
+ }
414
+ </script>
@@ -60,6 +60,28 @@
60
60
  </v-row>
61
61
  </v-col>
62
62
 
63
+ <v-col cols="12">
64
+ <v-row>
65
+ <v-col cols="12" class="mt-2">
66
+ <v-row no-gutters>
67
+ <InputLabel
68
+ class="text-capitalize font-weight-bold"
69
+ title="Owner"
70
+ />
71
+ <v-col cols="12">
72
+ <v-autocomplete
73
+ v-model="buildingUnit.ownerName"
74
+ :items="peopleItems"
75
+ item-title="name"
76
+ return-object
77
+ density="comfortable">
78
+ </v-autocomplete>
79
+ </v-col>
80
+ </v-row>
81
+ </v-col>
82
+ </v-row>
83
+ </v-col>
84
+
63
85
  <v-col cols="12">
64
86
  <v-row>
65
87
  <v-col cols="12" class="mt-2">
@@ -258,6 +280,8 @@ const prop = defineProps({
258
280
  _id: "",
259
281
  site: "",
260
282
  name: "",
283
+ owner: "",
284
+ ownerName: "",
261
285
  building: "",
262
286
  buildingName: "",
263
287
  category: "",
@@ -277,6 +301,8 @@ const buildingUnit = ref({
277
301
  _id: "",
278
302
  site: "",
279
303
  name: "",
304
+ owner: "",
305
+ ownerName: "",
280
306
  building: "",
281
307
  buildingName: "",
282
308
  level: 0,
@@ -297,8 +323,10 @@ const emit = defineEmits(["cancel", "success", "success:create-more"]);
297
323
  const validForm = ref(false);
298
324
 
299
325
  const { getAll } = useBuilding();
326
+ const { getPeopleByUnit } = usePeople();
300
327
 
301
328
  const buildings = ref<Record<string, any>[]>([]);
329
+ const peopleItems = ref<Array<Record<string, any>>>([]);
302
330
 
303
331
  const { data: getBuildingReq } = useLazyAsyncData(
304
332
  "get-all-buildings",
@@ -315,6 +343,17 @@ watchEffect(() => {
315
343
  }
316
344
  });
317
345
 
346
+ const { data: getUnitPeople } = useLazyAsyncData(
347
+ "get-unit-people",
348
+ async () => getPeopleByUnit(prop.roomFacility._id as string)
349
+ );
350
+
351
+ watch(getUnitPeople, (newData: any) => {
352
+ if (newData) {
353
+ peopleItems.value = newData ?? [];
354
+ }
355
+ });
356
+
318
357
  buildingUnit.value.site = prop.site;
319
358
 
320
359
  const selectedBuilding = computed(() => {
@@ -355,6 +394,12 @@ const hasChanges = computed(() => {
355
394
  async function submit() {
356
395
  disable.value = true;
357
396
  try {
397
+
398
+ if (buildingUnit.value.ownerName) {
399
+ buildingUnit.value.owner = (buildingUnit.value.ownerName as any)._id
400
+ buildingUnit.value.ownerName = (buildingUnit.value.ownerName as any).name
401
+ }
402
+
358
403
  await updateById(buildingUnit.value._id ?? "", {
359
404
  name: buildingUnit.value.name,
360
405
  level: buildingUnit.value.level,
@@ -364,6 +409,8 @@ async function submit() {
364
409
  companyRegistrationNumber: buildingUnit.value.companyRegistrationNumber || "",
365
410
  leaseStart: buildingUnit.value.leaseStart,
366
411
  leaseEnd: buildingUnit.value.leaseEnd,
412
+ owner: buildingUnit.value.owner || "",
413
+ ownerName: buildingUnit.value.ownerName || ""
367
414
  });
368
415
 
369
416
  emit("success");
@@ -15,10 +15,19 @@
15
15
  v-model="document.attachment"
16
16
  :multiple="false"
17
17
  :max-length="10"
18
- title="Upload Images"
18
+ title="Upload PDF Files"
19
+ accept=".pdf, .doc, .docx, .xls, .xlsx, .txt"
19
20
  />
20
21
  </v-col>
21
22
 
23
+ <v-col cols="12" class="px-6">
24
+ <InputLabel class="text-capitalize" title="Document Name" />
25
+ <v-text-field
26
+ v-model.trim="document.name"
27
+ density="comfortable"
28
+ ></v-text-field>
29
+ </v-col>
30
+
22
31
  <v-col cols="12">
23
32
  <v-row no-gutters>
24
33
  <v-col cols="12" class="text-center">
@@ -57,7 +66,12 @@
57
66
  color="black"
58
67
  class="text-none"
59
68
  size="48"
60
- :disabled="!validForm || disable"
69
+ :disabled="
70
+ !validForm ||
71
+ disable ||
72
+ document.attachment.length == 0 ||
73
+ document.name == ''
74
+ "
61
75
  @click="submit"
62
76
  :loading="disable"
63
77
  >
@@ -78,17 +92,42 @@ const prop = defineProps({
78
92
  type: Object as PropType<TDocument>,
79
93
  default: () => ({
80
94
  name: "",
81
- attachment: "",
95
+ attachment: [],
82
96
  }),
83
97
  },
84
98
  });
85
99
 
86
100
  const emit = defineEmits(["cancel", "success"]);
87
101
 
102
+ const { add, updateById } = useDocument();
103
+ const { getFileById } = useFile();
104
+
88
105
  const validForm = ref(false);
89
106
  const disable = ref(false);
90
107
  const message = ref("");
91
108
 
109
+ const document = ref<Record<string, any>>({
110
+ name: "",
111
+ attachment: [],
112
+ });
113
+
114
+ if (prop.mode === "edit") {
115
+ document.value.name = prop.document.name;
116
+ document.value.attachment = [prop.document.attachment];
117
+ }
118
+
119
+ watch(
120
+ () => document.value.attachment,
121
+ async (newVal) => {
122
+ if (newVal) {
123
+ const fileData = await getFileById(newVal);
124
+ if (fileData) {
125
+ document.value.name = fileData.data.name;
126
+ }
127
+ }
128
+ }
129
+ );
130
+
92
131
  function cancel() {
93
132
  // createMore.value = false;
94
133
  message.value = "";
@@ -97,5 +136,30 @@ function cancel() {
97
136
 
98
137
  async function submit() {
99
138
  disable.value = true;
139
+ try {
140
+ if (prop.mode === "add") {
141
+ const payload = {
142
+ name: document.value.name,
143
+ attachment: document.value.attachment,
144
+ status: "active",
145
+ };
146
+ await add(payload);
147
+ }
148
+
149
+ if (prop.mode === "edit") {
150
+ const payload = {
151
+ name: document.value.name,
152
+ attachment: document.value.attachment,
153
+ }
154
+ await updateById(prop.document._id ?? "", payload);
155
+ }
156
+
157
+ emit("success");
158
+ } catch (error: any) {
159
+ message.value =
160
+ error.response?._data?.message || "Failed to create document";
161
+ } finally {
162
+ disable.value = false;
163
+ }
100
164
  }
101
165
  </script>