@iservice365/layer-common 1.3.0 → 1.3.1

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,338 @@
1
+ <template>
2
+ <v-card width="100%">
3
+ <v-toolbar>
4
+ <v-row no-gutters class="fill-height px-6" align="center">
5
+ <span class="font-weight-bold text-h5"> Add Unit </span>
6
+ </v-row>
7
+ </v-toolbar>
8
+ <v-card-text style="max-height: 100vh; overflow-y: auto">
9
+ <v-form v-model="validForm" :disabled="disable">
10
+ <v-row no-gutters>
11
+ <v-col cols="12" class="mt-2">
12
+ <v-row no-gutters>
13
+ <InputLabel class="text-capitalize" title="Name" required />
14
+ <v-col cols="12">
15
+ <v-text-field
16
+ v-model="buildingUnit.name"
17
+ density="comfortable"
18
+ :rules="[requiredRule]"
19
+ ></v-text-field>
20
+ </v-col>
21
+ </v-row>
22
+ </v-col>
23
+
24
+ <v-col cols="12">
25
+ <v-row>
26
+ <v-col cols="12" class="mt-2">
27
+ <v-row no-gutters>
28
+ <InputLabel
29
+ class="text-capitalize"
30
+ title="Building"
31
+ required
32
+ />
33
+ <v-col cols="12">
34
+ <v-autocomplete
35
+ v-model="buildingUnit.building"
36
+ :items="buildings"
37
+ item-title="name"
38
+ item-value="_id"
39
+ v-model:search="searchBuilding"
40
+ :hide-no-data="false"
41
+ density="comfortable"
42
+ :rules="[requiredRule]"
43
+ variant="outlined"
44
+ ></v-autocomplete>
45
+ </v-col>
46
+ </v-row>
47
+ </v-col>
48
+ </v-row>
49
+ </v-col>
50
+
51
+ <v-col cols="12" class="mt-2">
52
+ <v-row no-gutters>
53
+ <InputLabel class="text-capitalize" title="Category" required />
54
+ <v-col cols="12">
55
+ <v-autocomplete
56
+ v-model="buildingUnit.category"
57
+ :items="unitCategories"
58
+ density="comfortable"
59
+ :rules="[requiredRule]"
60
+ ></v-autocomplete>
61
+ </v-col>
62
+ </v-row>
63
+ </v-col>
64
+
65
+ <v-col cols="12">
66
+ <v-row>
67
+ <v-col cols="6" class="mt-2">
68
+ <v-row no-gutters>
69
+ <InputLabel class="text-capitalize" title="Level" required />
70
+ <v-col cols="12">
71
+ <v-select
72
+ v-model="buildingUnit.level"
73
+ :items="buildingLevels"
74
+ density="comfortable"
75
+ :rules="[requiredRule]"
76
+ ></v-select>
77
+ </v-col>
78
+ </v-row>
79
+ </v-col>
80
+
81
+ <v-col cols="6" class="mt-2">
82
+ <v-row no-gutters>
83
+ <InputLabel
84
+ class="text-capitalize"
85
+ title="No. of Units"
86
+ required
87
+ />
88
+ <v-col cols="12">
89
+ <v-text-field
90
+ v-model.number="buildingUnitQty"
91
+ density="comfortable"
92
+ :rules="[requiredRule]"
93
+ type="number"
94
+ @update:model-value="setBuildingLevels()"
95
+ ></v-text-field>
96
+ </v-col>
97
+ </v-row>
98
+ </v-col>
99
+ </v-row>
100
+ </v-col>
101
+
102
+ <v-col v-if="buildingLevels" cols="12">
103
+ <v-row justify="center">
104
+ <v-col cols="6">
105
+ <v-btn
106
+ block
107
+ color="primary"
108
+ variant="text"
109
+ class="text-none font-weight-bold"
110
+ text="Set unit labels"
111
+ @click="show = !show"
112
+ ></v-btn>
113
+ </v-col>
114
+ </v-row>
115
+ </v-col>
116
+
117
+ <v-expand-transition>
118
+ <v-row v-show="show" no-gutters>
119
+ <template
120
+ v-for="(unitLabel, unitLabelIndex) in unitLabels"
121
+ :key="levelIndex"
122
+ >
123
+ <v-col cols="12">
124
+ <v-row no-gutters>
125
+ <InputLabel
126
+ class="text-capitalize font-weight-bold"
127
+ :title="`Unit ${unitLabelIndex + 1}`"
128
+ />
129
+ <v-col cols="12">
130
+ <v-text-field
131
+ v-model.trim="unitLabels[unitLabelIndex]"
132
+ density="comfortable"
133
+ ></v-text-field>
134
+ </v-col>
135
+ </v-row>
136
+ </v-col>
137
+ </template>
138
+ </v-row>
139
+ </v-expand-transition>
140
+
141
+ <v-col cols="12" class="mt-5">
142
+ <InputLabel class="text-capitalize" title="Upload Files (Lease of Contract, Cert. of Occupancy)" />
143
+ <InputFileV2 v-model="buildingUnit.buildingUnitFiles" :multiple="false" :max-length="10" title="Upload PDF Files" accept="application/pdf" />
144
+ </v-col>
145
+
146
+ <v-col cols="12" class="mt-2">
147
+ <v-checkbox v-model="createMore" density="comfortable" hide-details>
148
+ <template #label>
149
+ <span class="text-subtitle-2 font-weight-bold">
150
+ Create more
151
+ </span>
152
+ </template>
153
+ </v-checkbox>
154
+ </v-col>
155
+
156
+ <v-col cols="12" class="my-2">
157
+ <v-row no-gutters>
158
+ <v-col cols="12" class="text-center">
159
+ <span
160
+ class="text-none text-subtitle-2 font-weight-medium text-error"
161
+ >
162
+ {{ message }}
163
+ </span>
164
+ </v-col>
165
+ </v-row>
166
+ </v-col>
167
+ </v-row>
168
+ </v-form>
169
+ </v-card-text>
170
+
171
+ <v-toolbar density="compact">
172
+ <v-row no-gutters>
173
+ <v-col cols="6">
174
+ <v-btn
175
+ tile
176
+ block
177
+ variant="text"
178
+ class="text-none"
179
+ size="48"
180
+ @click="cancel"
181
+ :disabled="disable"
182
+ >
183
+ Cancel
184
+ </v-btn>
185
+ </v-col>
186
+
187
+ <v-col cols="6">
188
+ <v-btn
189
+ tile
190
+ block
191
+ variant="flat"
192
+ color="black"
193
+ class="text-none"
194
+ size="48"
195
+ :disabled="!validForm || disable"
196
+ @click="submit"
197
+ :loading="disable"
198
+ >
199
+ Submit
200
+ </v-btn>
201
+ </v-col>
202
+ </v-row>
203
+ </v-toolbar>
204
+ </v-card>
205
+ </template>
206
+
207
+ <script setup lang="ts">
208
+ const prop = defineProps({
209
+ site: {
210
+ type: String,
211
+ default: "",
212
+ },
213
+ });
214
+
215
+ const searchBuilding = ref("");
216
+
217
+ const emit = defineEmits(["cancel", "success", "success:create-more"]);
218
+
219
+ const validForm = ref(false);
220
+
221
+ const { getAll } = useBuilding();
222
+
223
+ const buildings = ref<TBuilding[]>([]);
224
+
225
+ const { data: getBuildingReq } = useLazyAsyncData(
226
+ "get-all-buildings",
227
+ async () => getAll({ site: prop.site, status: "active" })
228
+ );
229
+
230
+ watchEffect(() => {
231
+ if (getBuildingReq.value) {
232
+ buildings.value = getBuildingReq.value.items;
233
+ }
234
+ });
235
+
236
+ const buildingUnitQty = ref(1);
237
+
238
+ const buildingUnit = ref<TBuildingUnit>({
239
+ site: "",
240
+ name: "Unit",
241
+ building: "",
242
+ buildingName: "",
243
+ block: 0,
244
+ level: "",
245
+ category: "",
246
+ status: "active",
247
+ buildingUnitFiles: []
248
+ });
249
+
250
+ buildingUnit.value.site = prop.site;
251
+
252
+ const selectedBuilding = computed(() => {
253
+ return buildings.value.find((b) => b._id === buildingUnit.value.building);
254
+ });
255
+
256
+ watchEffect(() => {
257
+ if (buildingUnit.value.building) {
258
+ buildingUnit.value.buildingName = selectedBuilding.value?.name;
259
+ buildingUnit.value.block = selectedBuilding.value?.block || 0;
260
+ } else {
261
+ buildingUnit.value.buildingName = "";
262
+ buildingUnit.value.block = 0;
263
+ }
264
+ });
265
+
266
+ const buildingLevels = computed(() => {
267
+ return selectedBuilding.value?.levels || [];
268
+ });
269
+
270
+ const unitLabels = ref<string[]>([]);
271
+ unitLabels.value.push("Unit 1");
272
+
273
+ const show = ref(false);
274
+
275
+ function setBuildingLevels() {
276
+ unitLabels.value.length = 0;
277
+ for (let index = 0; index < buildingUnitQty.value; index++) {
278
+ unitLabels.value.push(`Unit ${index + 1}`);
279
+ }
280
+ }
281
+
282
+ const name = ref("");
283
+ const directorName = ref("");
284
+ const createMore = ref(false);
285
+ const disable = ref(false);
286
+
287
+ const { requiredRule } = useUtils();
288
+
289
+ const message = ref("");
290
+
291
+ const { add, categories: unitCategories } = useBuildingUnit();
292
+
293
+ function setBuildingUnit() {
294
+ buildingUnit.value.site = prop.site ?? "";
295
+ buildingUnit.value.name = "Unit";
296
+ buildingUnit.value.building = "";
297
+ buildingUnit.value.buildingName = "";
298
+ buildingUnit.value.level = "";
299
+ buildingUnit.value.category = "";
300
+ buildingUnit.value.status = "active";
301
+ buildingUnit.value.buildingUnitFiles = []
302
+ buildingUnitQty.value = 1;
303
+ message.value = "";
304
+ }
305
+
306
+ async function submit() {
307
+ disable.value = true;
308
+ try {
309
+ await add({
310
+ labels: unitLabels.value,
311
+ unit: buildingUnit.value,
312
+ qty: buildingUnitQty.value,
313
+ });
314
+
315
+ if (createMore.value) {
316
+ setBuildingUnit();
317
+ message.value = "";
318
+ emit("success:create-more");
319
+ return;
320
+ }
321
+
322
+ emit("success");
323
+ } catch (error: any) {
324
+ message.value =
325
+ error.response?._data?.message || "Failed to create building";
326
+ } finally {
327
+ disable.value = false;
328
+ }
329
+ }
330
+
331
+ function cancel() {
332
+ name.value = "";
333
+ directorName.value = "";
334
+ createMore.value = false;
335
+ message.value = "";
336
+ emit("cancel");
337
+ }
338
+ </script>
@@ -0,0 +1,278 @@
1
+ <template>
2
+ <v-card width="100%">
3
+ <v-toolbar>
4
+ <v-row no-gutters class="fill-height px-6" align="center">
5
+ <span class="font-weight-bold text-h5"> Edit Unit </span>
6
+ </v-row>
7
+ </v-toolbar>
8
+ <v-card-text style="max-height: 100vh; overflow-y: auto">
9
+ <v-form v-model="validForm" :disabled="disable">
10
+ <v-row no-gutters>
11
+ <v-col cols="12">
12
+ <v-row>
13
+ <v-col cols="12" class="mt-2">
14
+ <v-row no-gutters>
15
+ <InputLabel
16
+ class="text-capitalize font-weight-bold"
17
+ title="Building"
18
+ />
19
+ <v-col cols="12">
20
+ {{ buildingUnit.buildingName }}
21
+ </v-col>
22
+ </v-row>
23
+ </v-col>
24
+ </v-row>
25
+ </v-col>
26
+
27
+ <v-col cols="12">
28
+ <v-row>
29
+ <v-col cols="12" class="mt-2">
30
+ <v-row no-gutters>
31
+ <InputLabel
32
+ class="text-capitalize font-weight-bold"
33
+ title="Block"
34
+ />
35
+ <v-col cols="12">
36
+ {{ buildingUnit.block }}
37
+ </v-col>
38
+ </v-row>
39
+ </v-col>
40
+ </v-row>
41
+ </v-col>
42
+
43
+ <v-col cols="12">
44
+ <v-row>
45
+ <v-col cols="12" class="mt-2">
46
+ <v-row no-gutters>
47
+ <InputLabel
48
+ class="text-capitalize font-weight-bold"
49
+ title="Unit Name"
50
+ />
51
+ <v-col cols="12">
52
+ <v-text-field
53
+ v-model="buildingUnit.name"
54
+ density="comfortable"
55
+ :rules="[requiredRule]"
56
+ ></v-text-field>
57
+ </v-col>
58
+ </v-row>
59
+ </v-col>
60
+ </v-row>
61
+ </v-col>
62
+
63
+ <v-col cols="12" class="mt-2">
64
+ <v-row no-gutters>
65
+ <InputLabel
66
+ class="text-capitalize font-weight-bold"
67
+ title="Category"
68
+ required
69
+ />
70
+ <v-col cols="12">
71
+ <v-autocomplete
72
+ v-model="buildingUnit.category"
73
+ :items="unitCategories"
74
+ density="comfortable"
75
+ :rules="[requiredRule]"
76
+ ></v-autocomplete>
77
+ </v-col>
78
+ </v-row>
79
+ </v-col>
80
+
81
+ <v-col cols="12">
82
+ <v-row>
83
+ <v-col cols="6" class="mt-2">
84
+ <v-row no-gutters>
85
+ <InputLabel
86
+ class="text-capitalize font-weight-bold"
87
+ title="Level"
88
+ required
89
+ />
90
+ <v-col cols="12">
91
+ <v-select
92
+ v-model="buildingUnit.level"
93
+ :items="buildingLevels"
94
+ density="comfortable"
95
+ :rules="[requiredRule]"
96
+ ></v-select>
97
+ </v-col>
98
+ </v-row>
99
+ </v-col>
100
+ </v-row>
101
+ </v-col>
102
+
103
+ <v-col cols="12" class="mt-5">
104
+ <InputLabel class="text-capitalize" title="Upload Files (Lease of Contract, Cert. of Occupancy)" />
105
+ <InputFileV2 v-model="buildingUnit.buildingUnitFiles" :multiple="false" :max-length="10" title="Upload PDF Files" accept="application/pdf" />
106
+ </v-col>
107
+
108
+ <v-col cols="12" class="my-2">
109
+ <v-row no-gutters>
110
+ <v-col cols="12" class="text-center">
111
+ <span
112
+ class="text-none text-subtitle-2 font-weight-medium text-error"
113
+ >
114
+ {{ message }}
115
+ </span>
116
+ </v-col>
117
+ </v-row>
118
+ </v-col>
119
+ </v-row>
120
+ </v-form>
121
+ </v-card-text>
122
+
123
+ <v-toolbar density="compact">
124
+ <v-row no-gutters>
125
+ <v-col cols="6">
126
+ <v-btn
127
+ tile
128
+ block
129
+ variant="text"
130
+ class="text-none"
131
+ size="48"
132
+ @click="cancel"
133
+ >
134
+ Cancel
135
+ </v-btn>
136
+ </v-col>
137
+
138
+ <v-col cols="6">
139
+ <v-btn
140
+ tile
141
+ block
142
+ variant="flat"
143
+ color="black"
144
+ class="text-none"
145
+ size="48"
146
+ :disabled="!validForm || !hasChanges || disable"
147
+ @click="submit"
148
+ :loading="disable"
149
+ >
150
+ Submit
151
+ </v-btn>
152
+ </v-col>
153
+ </v-row>
154
+ </v-toolbar>
155
+ </v-card>
156
+ </template>
157
+
158
+ <script setup lang="ts">
159
+ const prop = defineProps({
160
+ site: {
161
+ type: String,
162
+ default: "",
163
+ },
164
+ roomFacility: {
165
+ type: Object as PropType<TBuildingUnit>,
166
+ default: () => ({
167
+ _id: "",
168
+ site: "",
169
+ name: "",
170
+ building: "",
171
+ buildingName: "",
172
+ category: "",
173
+ block: null,
174
+ level: 0,
175
+ status: "active",
176
+ buildingUnitFiles: []
177
+ }),
178
+ },
179
+ });
180
+
181
+ const buildingUnit = ref({
182
+ _id: "",
183
+ site: "",
184
+ name: "",
185
+ building: "",
186
+ buildingName: "",
187
+ level: 0,
188
+ category: "",
189
+ block: null,
190
+ status: "active",
191
+ buildingUnitFiles: []
192
+ });
193
+
194
+ buildingUnit.value = JSON.parse(JSON.stringify(prop.roomFacility));
195
+
196
+ const emit = defineEmits(["cancel", "success", "success:create-more"]);
197
+
198
+ const validForm = ref(false);
199
+
200
+ const { getAll } = useBuilding();
201
+
202
+ const buildings = ref<Record<string, any>[]>([]);
203
+
204
+ const { data: getBuildingReq } = useLazyAsyncData(
205
+ "get-all-buildings",
206
+ async () => getAll({ site: prop.site, status: "active" })
207
+ );
208
+
209
+ watchEffect(() => {
210
+ if (getBuildingReq.value) {
211
+ buildings.value = getBuildingReq.value.items.map((building: TBuilding) => ({
212
+ title: building.name,
213
+ value: building._id,
214
+ levels: building.levels,
215
+ }));
216
+ }
217
+ });
218
+
219
+ buildingUnit.value.site = prop.site;
220
+
221
+ const selectedBuilding = computed(() => {
222
+ return buildings.value.find((b) => b.value === buildingUnit.value.building);
223
+ });
224
+
225
+ const buildingLevels = computed(() => {
226
+ return Array.from(
227
+ { length: selectedBuilding.value?.levels || 0 },
228
+ (_, i) => i + 1
229
+ );
230
+ });
231
+
232
+ const disable = ref(false);
233
+
234
+ const { requiredRule } = useUtils();
235
+
236
+ const message = ref("");
237
+
238
+ const { updateById, categories: unitCategories } = useBuildingUnit();
239
+
240
+ function arraysAreEqual(a: any[], b: any[]): boolean {
241
+ return a.length === b.length && a.every((val, index) => val === b[index]);
242
+ }
243
+
244
+
245
+
246
+ const hasChanges = computed(() => {
247
+ return (
248
+ prop.roomFacility.name !== buildingUnit.value.name ||
249
+ prop.roomFacility.category !== buildingUnit.value.category ||
250
+ prop.roomFacility.level !== buildingUnit.value.level ||
251
+ !arraysAreEqual(prop.roomFacility.buildingUnitFiles, buildingUnit.value.buildingUnitFiles)
252
+ );
253
+ });
254
+
255
+ async function submit() {
256
+ disable.value = true;
257
+ try {
258
+ await updateById(buildingUnit.value._id ?? "", {
259
+ name: buildingUnit.value.name,
260
+ level: buildingUnit.value.level,
261
+ category: buildingUnit.value.category,
262
+ buildingUnitFiles: buildingUnit.value.buildingUnitFiles || []
263
+ });
264
+
265
+ emit("success");
266
+ } catch (error: any) {
267
+ message.value =
268
+ error.response?._data?.message || "Failed to create building";
269
+ } finally {
270
+ disable.value = false;
271
+ }
272
+ }
273
+
274
+ function cancel() {
275
+ message.value = "";
276
+ emit("cancel");
277
+ }
278
+ </script>
@@ -299,7 +299,7 @@ watchEffect(() => {
299
299
  serviceProviders.value = getAllReq.value.map((i: any) => ({
300
300
  title: i.nature.replace(/_/g, " "),
301
301
  subtitle: i.title,
302
- value: i._id.org,
302
+ value: i.nature
303
303
  }));
304
304
  }
305
305
  });
@@ -6,6 +6,7 @@
6
6
  :placeholder="placeholder"
7
7
  :counter="maxlength"
8
8
  @input="onInput"
9
+ :loading="loading"
9
10
  outlined
10
11
  clearable
11
12
  />
@@ -25,9 +26,15 @@ const props = defineProps({
25
26
  maxlength: {
26
27
  type: [Number, String],
27
28
  default: false
29
+ },
30
+ loading: {
31
+ type: Boolean,
32
+ default: false
28
33
  }
29
34
  })
30
35
 
36
+ const emit = defineEmits(['update:modelValue'])
37
+
31
38
  const model = defineModel({required: true, default: ""})
32
39
 
33
40
  function onInput(event) {
@@ -38,4 +45,9 @@ function onInput(event) {
38
45
 
39
46
  model.value = formatted
40
47
  }
48
+
49
+
50
+ watch(model, (newVal) => {
51
+ emit('update:modelValue:', newVal)
52
+ })
41
53
  </script>
@@ -43,7 +43,7 @@
43
43
  <!-- Data Table -->
44
44
  <v-data-table :headers="headers" :items="items" :item-value="itemValue" :items-per-page="itemsPerPage"
45
45
  fixed-header hide-default-footer :hide-default-header="!showHeader"
46
- @click:row="(_: any, data: any) => emits('row-click', data)" style="max-height: calc(100vh - (200px))">
46
+ @click:row="(_: any, data: any) => emits('row-click', data)" :style="`max-height: calc(100vh - (${offset}px))`">
47
47
  <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
48
48
  <slot :name="slotName" v-bind="slotProps" />
49
49
  </template>
@@ -103,6 +103,10 @@ const props = defineProps({
103
103
  extensionHeight: {
104
104
  type: Number,
105
105
  default: 50
106
+ },
107
+ offset: {
108
+ type: Number,
109
+ default: 200
106
110
  }
107
111
  });
108
112