@7365admin1/layer-common 1.10.9 → 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.
- package/CHANGELOG.md +6 -0
- package/components/BulletinBoardManagement.vue +18 -8
- package/components/DeliveryCompany.vue +240 -0
- package/components/EntryPassInformation.vue +35 -1
- package/components/FeedbackMain.vue +4 -19
- package/components/IncidentReport/Authorities.vue +189 -151
- package/components/IncidentReport/IncidentInformation.vue +14 -10
- package/components/IncidentReport/IncidentInformationDownload.vue +212 -0
- package/components/IncidentReport/affectedEntities.vue +8 -57
- package/components/SiteSettings.vue +285 -0
- package/components/Tooltip/Info.vue +33 -0
- package/components/VisitorForm.vue +48 -2
- package/components/VisitorManagement.vue +23 -6
- package/composables/useAccessManagement.ts +19 -0
- package/composables/useBulletin.ts +8 -3
- package/composables/useBulletinBoardPermission.ts +48 -0
- package/composables/useCleaningPermission.ts +2 -0
- package/composables/useCommonPermission.ts +29 -1
- package/composables/useFile.ts +6 -0
- package/composables/useSiteSettings.ts +1 -1
- package/composables/useVisitor.ts +6 -5
- package/constants/app.ts +12 -0
- package/nuxt.config.ts +2 -0
- package/package.json +2 -1
- package/plugins/vue-draggable-next.client.ts +5 -0
- package/types/site.d.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<v-row no-gutters>
|
|
3
3
|
<TableMain :headers="headers" :items="paginatePlaceholderItem" v-model:search="searchInput"
|
|
4
4
|
:loading="getAnnouncementPending" :page="page" :pages="pages" :pageRange="pageRange"
|
|
5
|
-
@refresh="getAnnouncementsRefresh" show-header @update:page="handleUpdatePage" @row-click="handleRowClick"
|
|
5
|
+
@refresh="getAnnouncementsRefresh" :show-header="APP_CONSTANTS.RESIDENT === props.recipients" @update:page="handleUpdatePage" @row-click="handleRowClick"
|
|
6
6
|
@create="handleCreateEvent" :can-create="canCreateBulletinBoard" create-label="Add Announcement">
|
|
7
7
|
<template #extension>
|
|
8
8
|
<v-row no-gutters class="w-100 d-flex flex-column">
|
|
@@ -60,6 +60,8 @@
|
|
|
60
60
|
</v-row>
|
|
61
61
|
</template>
|
|
62
62
|
<script setup lang="ts">
|
|
63
|
+
import { APP_CONSTANTS } from '../constants/app';
|
|
64
|
+
|
|
63
65
|
definePageMeta({
|
|
64
66
|
memberOnly: true,
|
|
65
67
|
})
|
|
@@ -104,13 +106,21 @@ const { debounce } = useUtils()
|
|
|
104
106
|
|
|
105
107
|
|
|
106
108
|
|
|
107
|
-
const headers =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
const headers = computed(() => {
|
|
110
|
+
const arr = [
|
|
111
|
+
{ title: "Title", value: "title", align: "start" },
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
if (props.recipients === APP_CONSTANTS.RESIDENT) {
|
|
115
|
+
arr.push({ title: "Date Created", value: "createdAt", align: "start" });
|
|
116
|
+
arr.push({ title: "Start Date/End Date", value: "duration", align: "start" });
|
|
117
|
+
arr.push({ title: "No Expiration", value: "noExpiration", align: "start" });
|
|
118
|
+
arr.push({ title: "", value: "actions", align: "center" });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return arr;
|
|
122
|
+
|
|
123
|
+
});
|
|
114
124
|
const items = ref<TAnnouncement[]>([]);
|
|
115
125
|
const page = ref(1);
|
|
116
126
|
const pages = ref(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>
|
|
@@ -189,6 +189,8 @@
|
|
|
189
189
|
</v-card>
|
|
190
190
|
</v-container>
|
|
191
191
|
</v-dialog>
|
|
192
|
+
|
|
193
|
+
<Snackbar v-model="snackbar" :text="snackbarText" :color="snackbarColor" />
|
|
192
194
|
</div>
|
|
193
195
|
</template>
|
|
194
196
|
|
|
@@ -226,7 +228,7 @@ const props = defineProps({
|
|
|
226
228
|
|
|
227
229
|
const emit = defineEmits(["update:modelValue", "update:quantity", "update:cards"]);
|
|
228
230
|
|
|
229
|
-
const { getAvailableContractorCards } = useAccessManagement();
|
|
231
|
+
const { getAvailableContractorCards, generateQrVms } = useAccessManagement();
|
|
230
232
|
|
|
231
233
|
const nfcEnabled = computed(() => props.settings?.data?.settings?.nfcPass ?? false);
|
|
232
234
|
const printer = computed(() => props.settings?.data?.settings?.printer ?? { vendorId: null, productId: null });
|
|
@@ -250,6 +252,17 @@ const selectedCards = computed({
|
|
|
250
252
|
},
|
|
251
253
|
});
|
|
252
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
|
+
|
|
253
266
|
// ─── Card fetching ────────────────────────────────────────────────
|
|
254
267
|
const cardItems = ref<any[]>([]);
|
|
255
268
|
const cardsLoading = ref(false);
|
|
@@ -266,6 +279,27 @@ async function fetchCards(type: "NFC" | "QRCODE" = "NFC") {
|
|
|
266
279
|
});
|
|
267
280
|
cardItems.value = res?.data?.[0]?.items ?? [];
|
|
268
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 = [];
|
|
269
303
|
} finally {
|
|
270
304
|
cardsLoading.value = false;
|
|
271
305
|
}
|
|
@@ -109,29 +109,14 @@
|
|
|
109
109
|
>
|
|
110
110
|
<template #item.createdBy="{ item, index }">
|
|
111
111
|
<v-avatar
|
|
112
|
-
v-if="
|
|
113
|
-
item?.createdBy &&
|
|
114
|
-
Boolean(item?.createdBy) &&
|
|
115
|
-
typeof item?.createdBy === 'object' &&
|
|
116
|
-
Object.keys(item?.createdBy).length > 0
|
|
117
|
-
"
|
|
112
|
+
v-if="item?.createdBy?.name"
|
|
118
113
|
size="small"
|
|
119
114
|
:color="materialColors[index % materialColors.length]"
|
|
120
115
|
class="text-subtitle-2 mr-1 mr-md-4"
|
|
121
116
|
>
|
|
122
|
-
{{
|
|
123
|
-
getInitial(
|
|
124
|
-
`${item?.createdBy?.givenName ?? ""} ${
|
|
125
|
-
item?.createdBy?.surname ?? ""
|
|
126
|
-
}`
|
|
127
|
-
) || ""
|
|
128
|
-
}}
|
|
117
|
+
{{ getInitial(item?.createdBy?.name) ?? "" }}
|
|
129
118
|
</v-avatar>
|
|
130
|
-
{{
|
|
131
|
-
`${item?.createdBy?.givenName ?? ""} ${
|
|
132
|
-
item?.createdBy?.surname ?? ""
|
|
133
|
-
}`
|
|
134
|
-
}}
|
|
119
|
+
{{ item?.createdBy?.name ?? "N/A" }}
|
|
135
120
|
</template>
|
|
136
121
|
<template #item.subject="{ item }">
|
|
137
122
|
{{ item.subject || "N/A" }}
|
|
@@ -685,7 +670,7 @@ const {
|
|
|
685
670
|
} = useUtils();
|
|
686
671
|
|
|
687
672
|
const headers: Array<Record<string, any>> = [
|
|
688
|
-
{ title: "Name", value: "
|
|
673
|
+
{ title: "Name", value: "createdBy", align: "start" },
|
|
689
674
|
{ title: "Subject", value: "subject", align: "start" },
|
|
690
675
|
{ title: "Date", value: "createdAt", align: "start" },
|
|
691
676
|
{ title: "App", value: "app", align: "start" },
|