@7365admin1/layer-common 3.1.0 → 3.1.2-staging.29

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.
@@ -34,3 +34,14 @@ jobs:
34
34
 
35
35
  - name: Publish to npm with staging tag
36
36
  run: npm publish --tag staging --registry https://registry.npmjs.org
37
+
38
+ - name: Notify Discord on failure
39
+ if: failure()
40
+ env:
41
+ DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
42
+ run: |
43
+ MESSAGE="@everyone :rotating_light: **Publish Staging** failed in \`${{ github.repository }}\` (\`${{ github.workflow }}\`)
44
+ ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
45
+ curl -s -X POST -H "Content-Type: application/json" \
46
+ -d "$(jq -n --arg content "$MESSAGE" '{content: $content}')" \
47
+ "$DISCORD_WEBHOOK_URL"
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 3.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - e3264a0: Update package version
8
+
3
9
  ## 3.1.0
4
10
 
5
11
  ### Minor Changes
@@ -12,6 +12,24 @@
12
12
  <v-card-text class="pa-4">
13
13
  <v-form ref="formRef" v-model="valid">
14
14
  <v-row no-gutters class="pa-0">
15
+ <v-col cols="12" class="pa-0 mb-2">
16
+ <PhotoUpload
17
+ v-model="attachmentPhotos"
18
+ v-model:photo-ids="attachmentIds"
19
+ title="Photo"
20
+ @error="photoUploadError = $event"
21
+ />
22
+
23
+ <v-alert
24
+ v-if="photoUploadError"
25
+ type="error"
26
+ density="compact"
27
+ variant="tonal"
28
+ class="mt-2"
29
+ >
30
+ {{ photoUploadError }}
31
+ </v-alert>
32
+ </v-col>
15
33
  <v-col cols="12" class="pa-0 mb-2">
16
34
  <InputLabel for="name" title="Name" required class="mb-1" />
17
35
  <v-text-field
@@ -87,6 +105,10 @@ const unitOfMeasurementModel = defineModel("unitOfMeasurement", {
87
105
  type: String,
88
106
  default: "",
89
107
  });
108
+ const attachmentModel = defineModel("attachment", {
109
+ type: String,
110
+ default: "",
111
+ });
90
112
  const showDialog = defineModel({ type: Boolean, default: false });
91
113
 
92
114
  const prop = defineProps({
@@ -105,6 +127,9 @@ const emit = defineEmits(["saved", "close"]);
105
127
  const formRef = ref<any>(null);
106
128
  const valid = ref(false);
107
129
  const submitting = ref(false);
130
+ const attachmentPhotos = ref<string[]>([]);
131
+ const attachmentIds = ref<string[]>([]);
132
+ const photoUploadError = ref("");
108
133
 
109
134
  const { requiredRule } = useUtils();
110
135
 
@@ -124,6 +149,29 @@ const unitOfMeasurementOptions = [
124
149
  watchEffect(() => {
125
150
  nameModel.value = prop.equipmentData?.name || "";
126
151
  unitOfMeasurementModel.value = prop.equipmentData?.unitOfMeasurement || "";
152
+ attachmentModel.value = prop.equipmentData?.attachment || "";
153
+ });
154
+
155
+ watch(
156
+ attachmentIds,
157
+ (photoIds) => {
158
+ const [attachmentId] = photoIds;
159
+ attachmentModel.value = attachmentId || "";
160
+
161
+ if (photoIds.length > 1) {
162
+ attachmentIds.value = attachmentId ? [attachmentId] : [];
163
+ attachmentPhotos.value = attachmentPhotos.value.slice(0, 1);
164
+ }
165
+ },
166
+ { deep: true }
167
+ );
168
+
169
+ watch(showDialog, (isOpen) => {
170
+ if (!isOpen) {
171
+ attachmentPhotos.value = [];
172
+ attachmentIds.value = [];
173
+ photoUploadError.value = "";
174
+ }
127
175
  });
128
176
 
129
177
  function close() {
@@ -147,12 +195,15 @@ async function submit() {
147
195
 
148
196
  emit("saved", {
149
197
  name: nameModel.value,
150
-
151
198
  unitOfMeasurement: unitOfMeasurementModel.value,
199
+ attachment: attachmentModel.value,
152
200
  });
153
201
 
154
202
  nameModel.value = "";
155
203
  unitOfMeasurementModel.value = "";
204
+ attachmentModel.value = "";
205
+ attachmentPhotos.value = [];
206
+ attachmentIds.value = [];
156
207
 
157
208
  showDialog.value = false;
158
209
  emit("close");
@@ -32,11 +32,6 @@
32
32
  color="blue"
33
33
  variant="tonal"
34
34
  size="small"
35
- closable
36
- @click:close="
37
- existingPassToBeReplaced = existingPass;
38
- existingPass = null;
39
- "
40
35
  >
41
36
  {{ existingPass.prefixAndName }}
42
37
  </v-chip>
@@ -45,9 +40,9 @@
45
40
 
46
41
  <!-- New pass selector -->
47
42
  <v-autocomplete
48
- v-if="!existingPass"
49
43
  v-model="selectedPass"
50
44
  v-model:search="passInput"
45
+ :label="existingPass?.keyId ? 'Change pass' : undefined"
51
46
  :hide-no-data="false"
52
47
  :items="passItems"
53
48
  item-title="prefixAndName"
@@ -85,20 +80,20 @@
85
80
  class="d-flex flex-wrap ga-1 mb-2"
86
81
  >
87
82
  <v-chip
88
- v-for="key in existingKeys"
83
+ v-for="key in existingKeys.filter((k) => k.status != 'Removed')"
89
84
  :key="key.keyId"
90
85
  prepend-icon="mdi-key"
91
86
  color="orange"
92
87
  variant="tonal"
93
88
  size="small"
94
89
  closable
95
- @click:close="removeExistingKey(key.keyId)"
90
+ @click:close="removeExistingKey(key)"
96
91
  >
97
92
  {{ key.prefixAndName }}
98
93
  </v-chip>
99
- <span class="text-caption text-medium-emphasis align-self-center"
100
- >(current)</span
101
- >
94
+ <span class="text-caption text-medium-emphasis align-self-center">
95
+ (current)
96
+ </span>
102
97
  </div>
103
98
 
104
99
  <!-- New keys selector -->
@@ -116,6 +111,8 @@
116
111
  density="compact"
117
112
  small-chips
118
113
  :loading="fetchKeysPending"
114
+ clearable
115
+ @update:model-value="selectKeys"
119
116
  >
120
117
  <template v-slot:chip="{ props: chipProps, item }">
121
118
  <v-chip
@@ -205,7 +202,7 @@ const processing = ref(false);
205
202
  const errorMessage = ref("");
206
203
 
207
204
  // New selections
208
- const selectedPass = ref<string>("");
205
+ const selectedPass = ref<string | null>(null);
209
206
  const selectedKeys = ref<string[]>([]);
210
207
  const passInput = ref("");
211
208
  const keyInput = ref("");
@@ -213,12 +210,15 @@ const passItems = ref<TPassKey[]>([]);
213
210
  const keyItems = ref<TPassKey[]>([]);
214
211
 
215
212
  // Existing pass/keys (editable)
216
- const existingPass = ref<{ keyId: string; prefixAndName: string } | null>(null);
217
- const existingPassToBeReplaced = ref<{
213
+ const existingPass = ref<{
218
214
  keyId: string;
219
215
  prefixAndName: string;
216
+ status?: string;
220
217
  } | null>(null);
221
- const existingKeys = ref<{ keyId: string; prefixAndName: string }[]>([]);
218
+
219
+ const existingKeys = ref<
220
+ { keyId: string; prefixAndName: string; status?: string }[]
221
+ >([]);
222
222
 
223
223
  const isEditMode = computed(() => prop.mode === "edit");
224
224
  const showKeys = computed(() => prop.visitor.type === "contractor");
@@ -237,28 +237,82 @@ const passTypesComputed = computed(() => {
237
237
  // Initialise existing values in edit mode
238
238
  onMounted(() => {
239
239
  if (isEditMode.value) {
240
- const vPass = prop.visitor.visitorPass;
240
+ const vPass = prop.visitor.visitorPass?.filter(
241
+ (i) => i.status != "Removed"
242
+ );
241
243
  if (Array.isArray(vPass) && vPass.length > 0) {
242
244
  const p = vPass[0] as any;
243
- existingPass.value = { keyId: p.keyId, prefixAndName: p.prefixAndName };
245
+ existingPass.value = {
246
+ keyId: p.keyId,
247
+ prefixAndName: p.prefixAndName,
248
+ status: p.status,
249
+ };
244
250
  }
245
- const vKeys = prop.visitor.passKeys;
251
+ const vKeys = prop.visitor.passKeys?.filter((i) => i.status != "Removed");
246
252
  if (Array.isArray(vKeys) && vKeys.length > 0) {
247
253
  existingKeys.value = (vKeys as any[]).map((k) => ({
248
254
  keyId: k.keyId,
249
255
  prefixAndName: k.prefixAndName,
256
+ status: k.status,
250
257
  }));
251
258
  }
252
259
  }
253
260
  });
254
261
 
255
- function removeExistingKey(keyId: string) {
256
- existingKeys.value = existingKeys.value.filter((k) => k.keyId !== keyId);
262
+ const previouslyRemovedKeys = ref<any>([]);
263
+
264
+ function removeExistingKey(key: Record<string, any>) {
265
+ const originalKey = existingKeys.value.find((k) => k.keyId === key.keyId);
266
+
267
+ if (
268
+ originalKey &&
269
+ !previouslyRemovedKeys.value.some((k: any) => k.keyId === key.keyId)
270
+ ) {
271
+ previouslyRemovedKeys.value.push({ ...originalKey });
272
+ }
273
+
274
+ existingKeys.value = existingKeys.value.map((i) => {
275
+ if (i.keyId === key.keyId) {
276
+ return {
277
+ ...i,
278
+ status: "Removed",
279
+ };
280
+ }
281
+ return i;
282
+ });
283
+ }
284
+
285
+ function selectKeys() {
286
+ const currentSelections = [...selectedKeys.value];
287
+
288
+ currentSelections.forEach((selectedId) => {
289
+ const removedMatch = previouslyRemovedKeys.value.find(
290
+ (k: any) => k.keyId === selectedId
291
+ );
292
+
293
+ if (removedMatch) {
294
+ existingKeys.value = existingKeys.value.map((i) => {
295
+ if (i.keyId === selectedId) {
296
+ return {
297
+ ...i,
298
+ status: removedMatch.status,
299
+ };
300
+ }
301
+ return i;
302
+ });
303
+
304
+ previouslyRemovedKeys.value = previouslyRemovedKeys.value.filter(
305
+ (k: any) => k.keyId !== selectedId
306
+ );
307
+
308
+ selectedKeys.value = selectedKeys.value.filter((id) => id !== selectedId);
309
+ }
310
+ });
257
311
  }
258
312
 
259
313
  // Exclude already-held pass/keys from selectable lists
260
314
  const existingPassId = computed(() => existingPass.value?.keyId);
261
- const existingKeyIds = computed(() => existingKeys.value.map((k) => k.keyId));
315
+ // const existingKeyIds = computed(() => existingKeys.value.map((k) => k.keyId));
262
316
 
263
317
  const {
264
318
  data: passesData,
@@ -294,43 +348,50 @@ const {
294
348
  })
295
349
  );
296
350
 
297
- watch(passesData, (data: any) => {
298
- const all: TPassKey[] = Array.isArray(data?.items) ? data.items : [];
299
- passItems.value = all.filter((p) => p._id !== existingPassId.value);
300
- });
351
+ // watch(passesData, (data: any) => {
352
+ // const all: TPassKey[] = Array.isArray(data?.items) ? data.items : [];
353
+ // passItems.value = all.filter((p) => p._id !== existingPassId.value);
354
+ // });
301
355
 
302
- watch(keysData, (data: any) => {
303
- const all: TPassKey[] = Array.isArray(data?.items) ? data.items : [];
304
- keyItems.value = all.filter((k) => !existingKeyIds.value.includes(k._id!));
305
- });
356
+ // watch(keysData, (data: any) => {
357
+ // const all: TPassKey[] = Array.isArray(data?.items) ? data.items : [];
358
+ // keyItems.value = all.filter((k) => !existingKeyIds.value.includes(k._id!));
359
+ // });
306
360
 
307
361
  // Re-filter when existing items change
308
362
  watch(
309
- [existingPassId, existingKeyIds],
363
+ [existingPassId, existingKeys, passesData, keysData],
310
364
  () => {
311
365
  const rawPasses = (passesData.value as any)?.items ?? [];
312
366
  passItems.value = rawPasses.filter(
313
367
  (p: TPassKey) => p._id !== existingPassId.value
314
368
  );
369
+
315
370
  const rawKeys = (keysData.value as any)?.items ?? [];
316
- keyItems.value = rawKeys.filter(
317
- (k: TPassKey) => !existingKeyIds.value.includes(k._id!)
371
+ const manuallyRemovedKeys = existingKeys.value
372
+ .filter((k) => k.status === "Removed")
373
+ .map((k) => ({
374
+ _id: k.keyId,
375
+ prefixAndName: k.prefixAndName,
376
+ }));
377
+
378
+ const allExistingKeyIds = existingKeys.value.map((k) => k.keyId);
379
+
380
+ const filteredRawKeys = rawKeys.filter(
381
+ (k: TPassKey) => !allExistingKeyIds.includes(k._id!)
318
382
  );
383
+
384
+ keyItems.value = [...filteredRawKeys, ...manuallyRemovedKeys];
385
+ console.log("keyItems: ", keyItems.value);
319
386
  },
320
- { deep: true }
387
+ { deep: true, immediate: true }
321
388
  );
322
389
 
323
390
  const canSubmit = computed(() => {
324
- if (isEditMode.value) {
325
- // allow save if anything changed: existing items modified OR new items selected
326
- return (
327
- existingPass.value !== null ||
328
- selectedPass.value ||
329
- existingKeys.value.length > 0 ||
330
- selectedKeys.value.length > 0
331
- );
332
- }
333
- return !!selectedPass.value || selectedKeys.value.length > 0;
391
+ const hasRemovedKeys = existingKeys.value.some((k) => k.status === "Removed");
392
+ return (
393
+ !!selectedPass.value || selectedKeys.value.length > 0 || hasRemovedKeys
394
+ );
334
395
  });
335
396
 
336
397
  async function handleSubmit() {
@@ -343,16 +404,13 @@ async function handleSubmit() {
343
404
 
344
405
  // Build final pass list: kept existing + newly selected
345
406
  const finalPasses: { keyId: string; status?: string; add?: boolean }[] = [];
346
- if (existingPass.value) {
347
- finalPasses.push({ keyId: existingPass.value.keyId, status: "Removed" });
348
- }
349
- if (existingPassToBeReplaced.value) {
350
- finalPasses.push({
351
- keyId: existingPassToBeReplaced.value.keyId,
352
- status: "Removed",
353
- });
354
- }
355
407
  if (selectedPass.value) {
408
+ if (existingPass.value) {
409
+ finalPasses.push({
410
+ keyId: existingPass.value.keyId,
411
+ status: "Removed",
412
+ });
413
+ }
356
414
  finalPasses.push({
357
415
  keyId: selectedPass.value,
358
416
  status: "Not Returned",
@@ -364,13 +422,17 @@ async function handleSubmit() {
364
422
  // Build final keys list: kept existing + newly selected
365
423
  if (showKeys.value) {
366
424
  const finalKeys: { keyId: string; status?: string; add?: boolean }[] = [
367
- ...existingKeys.value.map((k) => ({ keyId: k.keyId })),
425
+ ...existingKeys.value.map((k) => ({
426
+ keyId: k.keyId,
427
+ status: k.status,
428
+ })),
368
429
  ...selectedKeys.value.map((keyId) => ({
369
430
  keyId,
370
431
  status: "Not Returned",
371
432
  add: true,
372
433
  })),
373
434
  ];
435
+
374
436
  payload.passKeys = finalKeys;
375
437
  }
376
438