@iservice365/layer-common 1.5.6 → 1.6.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.
- package/CHANGELOG.md +12 -0
- package/components/AccessManagement.vue +136 -0
- package/components/DashboardPlaceholder.vue +91 -130
- package/components/DocumentForm.vue +67 -3
- package/components/DocumentManagement.vue +188 -4
- package/components/Editor.vue +59 -25
- package/components/Input/InputPhoneNumberV2.vue +1 -0
- package/components/SupplyManagement.vue +1 -1
- package/composables/useDashboardData.ts +425 -0
- package/composables/useDocument.ts +33 -13
- package/composables/useFile.ts +11 -5
- package/package.json +1 -1
- package/types/document.d.ts +1 -1
|
@@ -15,10 +15,19 @@
|
|
|
15
15
|
v-model="document.attachment"
|
|
16
16
|
:multiple="false"
|
|
17
17
|
:max-length="10"
|
|
18
|
-
title="Upload
|
|
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="
|
|
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>
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
>
|
|
25
25
|
<v-toolbar density="compact" color="grey-lighten-4">
|
|
26
26
|
<template #prepend>
|
|
27
|
-
<v-btn fab icon density="comfortable" @click="">
|
|
27
|
+
<v-btn fab icon density="comfortable" @click="getDocuments">
|
|
28
28
|
<v-icon>mdi-refresh</v-icon>
|
|
29
29
|
</v-btn>
|
|
30
30
|
</template>
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
<local-pagination
|
|
38
38
|
v-model="page"
|
|
39
39
|
:length="pages"
|
|
40
|
-
@update:value=""
|
|
40
|
+
@update:value="getDocuments"
|
|
41
41
|
/>
|
|
42
42
|
</v-row>
|
|
43
43
|
</template>
|
|
@@ -60,6 +60,154 @@
|
|
|
60
60
|
<v-dialog v-model="createDialog" width="450" persistent>
|
|
61
61
|
<DocumentForm @cancel="createDialog = false" @success="successCreate()" />
|
|
62
62
|
</v-dialog>
|
|
63
|
+
|
|
64
|
+
<!-- Edit Dialog -->
|
|
65
|
+
<v-dialog v-model="editDialog" width="450" persistent>
|
|
66
|
+
<DocumentForm
|
|
67
|
+
mode="edit"
|
|
68
|
+
@cancel="editDialog = false"
|
|
69
|
+
@success="successUpdate()"
|
|
70
|
+
:document="selectedDocument"
|
|
71
|
+
/>
|
|
72
|
+
</v-dialog>
|
|
73
|
+
|
|
74
|
+
<!-- Preview Dialog -->
|
|
75
|
+
<v-dialog v-model="previewDialog" width="450" persistent>
|
|
76
|
+
<v-card width="100%">
|
|
77
|
+
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
|
|
78
|
+
<v-row no-gutters class="mb-4">
|
|
79
|
+
<v-col cols="12">
|
|
80
|
+
<strong>Name:</strong> {{ selectedDocument?.name ?? "N/A" }}
|
|
81
|
+
</v-col>
|
|
82
|
+
|
|
83
|
+
<v-col cols="12">
|
|
84
|
+
<strong>Document: </strong>
|
|
85
|
+
<NuxtLink
|
|
86
|
+
:to="getFileUrl(selectedDocument.attachment)"
|
|
87
|
+
external
|
|
88
|
+
target="_blank"
|
|
89
|
+
>
|
|
90
|
+
View Document
|
|
91
|
+
</NuxtLink>
|
|
92
|
+
</v-col>
|
|
93
|
+
</v-row>
|
|
94
|
+
</v-card-text>
|
|
95
|
+
<v-toolbar class="pa-0" density="compact">
|
|
96
|
+
<v-row no-gutters>
|
|
97
|
+
<v-col cols="6" class="pa-0">
|
|
98
|
+
<v-btn
|
|
99
|
+
block
|
|
100
|
+
variant="text"
|
|
101
|
+
class="text-none"
|
|
102
|
+
size="large"
|
|
103
|
+
@click="previewDialog = false"
|
|
104
|
+
height="48"
|
|
105
|
+
>
|
|
106
|
+
Close
|
|
107
|
+
</v-btn>
|
|
108
|
+
</v-col>
|
|
109
|
+
<v-col cols="6" class="pa-0" v-if="canUpdate">
|
|
110
|
+
<v-menu>
|
|
111
|
+
<template #activator="{ props }">
|
|
112
|
+
<v-btn
|
|
113
|
+
block
|
|
114
|
+
variant="flat"
|
|
115
|
+
color="black"
|
|
116
|
+
class="text-none"
|
|
117
|
+
height="48"
|
|
118
|
+
v-bind="props"
|
|
119
|
+
tile
|
|
120
|
+
:disabled="!canUpdateDocument && !canDeleteDocument"
|
|
121
|
+
>
|
|
122
|
+
More actions
|
|
123
|
+
</v-btn>
|
|
124
|
+
</template>
|
|
125
|
+
<v-list class="pa-0">
|
|
126
|
+
<v-list-item
|
|
127
|
+
v-if="canUpdateDocument"
|
|
128
|
+
@click="openEditDialog()"
|
|
129
|
+
>
|
|
130
|
+
<v-list-item-title class="text-subtitle-2">
|
|
131
|
+
Edit Document
|
|
132
|
+
</v-list-item-title>
|
|
133
|
+
</v-list-item>
|
|
134
|
+
|
|
135
|
+
<v-list-item
|
|
136
|
+
v-if="canDeleteDocument"
|
|
137
|
+
@click="openDeleteDialog()"
|
|
138
|
+
class="text-red"
|
|
139
|
+
>
|
|
140
|
+
<v-list-item-title class="text-subtitle-2">
|
|
141
|
+
Delete Document
|
|
142
|
+
</v-list-item-title>
|
|
143
|
+
</v-list-item>
|
|
144
|
+
</v-list>
|
|
145
|
+
</v-menu>
|
|
146
|
+
</v-col>
|
|
147
|
+
</v-row>
|
|
148
|
+
</v-toolbar>
|
|
149
|
+
</v-card>
|
|
150
|
+
</v-dialog>
|
|
151
|
+
|
|
152
|
+
<!-- Delete Dialog -->
|
|
153
|
+
<v-dialog
|
|
154
|
+
v-model="confirmDialog"
|
|
155
|
+
:loading="deleteLoading"
|
|
156
|
+
width="450"
|
|
157
|
+
persistent
|
|
158
|
+
>
|
|
159
|
+
<v-card width="100%">
|
|
160
|
+
<v-toolbar density="compact" class="pl-4">
|
|
161
|
+
<span class="font-weight-medium text-h5">Delete Document</span>
|
|
162
|
+
</v-toolbar>
|
|
163
|
+
<v-card-text>
|
|
164
|
+
<p class="text-subtitle-2 text-center">
|
|
165
|
+
Are you sure you want to delete this document? This action cannot be
|
|
166
|
+
undone.
|
|
167
|
+
</p>
|
|
168
|
+
|
|
169
|
+
<v-row v-if="message" no-gutters justify="center" class="mt-4">
|
|
170
|
+
<span class="text-caption text-error text-center">
|
|
171
|
+
{{ message }}
|
|
172
|
+
</span>
|
|
173
|
+
</v-row>
|
|
174
|
+
</v-card-text>
|
|
175
|
+
|
|
176
|
+
<v-toolbar density="compact">
|
|
177
|
+
<v-row no-gutters>
|
|
178
|
+
<v-col cols="6">
|
|
179
|
+
<v-btn
|
|
180
|
+
tile
|
|
181
|
+
block
|
|
182
|
+
size="48"
|
|
183
|
+
variant="text"
|
|
184
|
+
class="text-none"
|
|
185
|
+
@click="confirmDialog = false"
|
|
186
|
+
:disabled="deleteLoading"
|
|
187
|
+
>
|
|
188
|
+
Close
|
|
189
|
+
</v-btn>
|
|
190
|
+
</v-col>
|
|
191
|
+
<v-col cols="6">
|
|
192
|
+
<v-btn
|
|
193
|
+
tile
|
|
194
|
+
block
|
|
195
|
+
size="48"
|
|
196
|
+
color="black"
|
|
197
|
+
variant="flat"
|
|
198
|
+
class="text-none"
|
|
199
|
+
@click="handleDeleteDocument"
|
|
200
|
+
:loading="deleteLoading"
|
|
201
|
+
>
|
|
202
|
+
Delete Document
|
|
203
|
+
</v-btn>
|
|
204
|
+
</v-col>
|
|
205
|
+
</v-row>
|
|
206
|
+
</v-toolbar>
|
|
207
|
+
</v-card>
|
|
208
|
+
</v-dialog>
|
|
209
|
+
|
|
210
|
+
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
63
211
|
</v-row>
|
|
64
212
|
</template>
|
|
65
213
|
<script setup lang="ts">
|
|
@@ -105,7 +253,8 @@ const props = defineProps({
|
|
|
105
253
|
});
|
|
106
254
|
|
|
107
255
|
const { headerSearch } = useLocal();
|
|
108
|
-
const { getAll: _getAllDocuments } = useDocument();
|
|
256
|
+
const { getAll: _getAllDocuments, deleteById } = useDocument();
|
|
257
|
+
const { getFileUrl } = useFile();
|
|
109
258
|
|
|
110
259
|
const page = ref(1);
|
|
111
260
|
const pages = ref(0);
|
|
@@ -149,8 +298,11 @@ const previewDialog = ref(false);
|
|
|
149
298
|
const selectedDocument = ref<TDocument>({
|
|
150
299
|
_id: "",
|
|
151
300
|
name: "",
|
|
152
|
-
attachment:
|
|
301
|
+
attachment: "",
|
|
153
302
|
});
|
|
303
|
+
const deleteLoading = ref(false);
|
|
304
|
+
const confirmDialog = ref(false);
|
|
305
|
+
const selectedDocumentId = ref<string | null>(null);
|
|
154
306
|
|
|
155
307
|
function tableRowClickHandler(_: any, data: any) {
|
|
156
308
|
selectedDocument.value = data.item as TDocument;
|
|
@@ -184,4 +336,36 @@ function successCreate() {
|
|
|
184
336
|
getDocuments();
|
|
185
337
|
showMessage("Document created successfully!", "success");
|
|
186
338
|
}
|
|
339
|
+
|
|
340
|
+
function openDeleteDialog() {
|
|
341
|
+
confirmDialog.value = true;
|
|
342
|
+
message.value = "";
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function openEditDialog() {
|
|
346
|
+
editDialog.value = true;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function handleDeleteDocument() {
|
|
350
|
+
deleteLoading.value = true;
|
|
351
|
+
try {
|
|
352
|
+
await deleteById(selectedDocument.value._id ?? "");
|
|
353
|
+
await getDocuments();
|
|
354
|
+
selectedDocumentId.value = null;
|
|
355
|
+
confirmDialog.value = false;
|
|
356
|
+
previewDialog.value = false;
|
|
357
|
+
} catch (error: any) {
|
|
358
|
+
message.value =
|
|
359
|
+
error?.response?._data?.message || "Failed to delete document";
|
|
360
|
+
} finally {
|
|
361
|
+
deleteLoading.value = false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function successUpdate() {
|
|
366
|
+
editDialog.value = false;
|
|
367
|
+
previewDialog.value = false;
|
|
368
|
+
getDocuments();
|
|
369
|
+
showMessage("Document updated successfully!", "success");
|
|
370
|
+
};
|
|
187
371
|
</script>
|
package/components/Editor.vue
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
<ClientOnly>
|
|
3
|
+
<v-row no-gutters class="ckeditor-container w-100">
|
|
4
|
+
<ckeditor v-model="data" :editor="ClassicEditor" :config="config" @ready="onReady" :key="prop.readOnly" />
|
|
5
|
+
</v-row>
|
|
6
|
+
</ClientOnly>
|
|
7
7
|
</template>
|
|
8
8
|
|
|
9
9
|
<script setup>
|
|
10
|
+
|
|
11
|
+
const prop = defineProps({
|
|
12
|
+
readOnly: {
|
|
13
|
+
type: Boolean,
|
|
14
|
+
required: false
|
|
15
|
+
}
|
|
16
|
+
})
|
|
10
17
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
ClassicEditor,
|
|
19
|
+
Essentials,
|
|
20
|
+
Paragraph,
|
|
21
|
+
Bold,
|
|
22
|
+
Italic,
|
|
23
|
+
Heading,
|
|
24
|
+
List,
|
|
25
|
+
Link,
|
|
26
|
+
Alignment,
|
|
27
|
+
Font,
|
|
21
28
|
} from 'ckeditor5'
|
|
22
29
|
import { Ckeditor } from '@ckeditor/ckeditor5-vue'
|
|
23
30
|
import 'ckeditor5/ckeditor5.css';
|
|
@@ -26,17 +33,45 @@ import 'ckeditor5/ckeditor5.css'
|
|
|
26
33
|
|
|
27
34
|
const data = defineModel({ required: true, default: "" })
|
|
28
35
|
|
|
36
|
+
const editorInstance = ref(null);
|
|
37
|
+
|
|
29
38
|
const config = computed(() => ({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
licenseKey: 'GPL',
|
|
40
|
+
plugins: [Essentials, Paragraph, Bold, Italic, Heading, List, Alignment, Font],
|
|
41
|
+
toolbar: [
|
|
42
|
+
'undo', 'redo',
|
|
43
|
+
'|', 'bold', 'italic',
|
|
44
|
+
'|', 'fontSize', 'fontColor', 'fontBackgroundColor',
|
|
45
|
+
'|', 'bulletedList', 'numberedList',
|
|
46
|
+
'|', 'heading', '|', 'alignment:left', 'alignment:center', 'alignment:right', 'alignment:justify',
|
|
47
|
+
],
|
|
39
48
|
}))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
function onReady(editor) {
|
|
52
|
+
editorInstance.value = editor;
|
|
53
|
+
toggleReadOnly(prop.readOnly);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function toggleReadOnly(isReadOnly) {
|
|
57
|
+
if (!editorInstance.value) return;
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if (isReadOnly) {
|
|
61
|
+
editorInstance.value.enableReadOnlyMode("view-mode-lock");
|
|
62
|
+
} else {
|
|
63
|
+
editorInstance.value.disableReadOnlyMode("view-mode-lock");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
watch(
|
|
68
|
+
() => prop.readOnly,
|
|
69
|
+
(newVal) => {
|
|
70
|
+
toggleReadOnly(newVal);
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
|
|
40
75
|
</script>
|
|
41
76
|
|
|
42
77
|
<style scoped>
|
|
@@ -58,4 +93,3 @@ const config = computed(() => ({
|
|
|
58
93
|
box-sizing: border-box;
|
|
59
94
|
}
|
|
60
95
|
</style>
|
|
61
|
-
|
|
@@ -58,7 +58,7 @@ const loading = ref(false);
|
|
|
58
58
|
|
|
59
59
|
const page = ref(1);
|
|
60
60
|
const pages = ref(0);
|
|
61
|
-
const pageRange = ref("
|
|
61
|
+
const pageRange = ref("1-10 of 10");
|
|
62
62
|
const items = ref<Array<Record<string, any>>>([]);
|
|
63
63
|
|
|
64
64
|
const headers: Array<Record<string, any>> = [
|