@iservice365/layer-common 1.5.5 → 1.5.7

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,101 @@
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 text-capitalize">
6
+ {{ prop.mode }} Document
7
+ </span>
8
+ </v-row>
9
+ </v-toolbar>
10
+ <v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-0">
11
+ <v-form v-model="validForm" :disabled="disable">
12
+ <v-col cols="12" class="px-6">
13
+ <InputLabel class="text-capitalize" title="Document Attachment" />
14
+ <InputFileV2
15
+ v-model="document.attachment"
16
+ :multiple="false"
17
+ :max-length="10"
18
+ title="Upload Images"
19
+ />
20
+ </v-col>
21
+
22
+ <v-col cols="12">
23
+ <v-row no-gutters>
24
+ <v-col cols="12" class="text-center">
25
+ <span
26
+ class="text-none text-subtitle-2 font-weight-medium text-error"
27
+ >
28
+ {{ message }}
29
+ </span>
30
+ </v-col>
31
+ </v-row>
32
+ </v-col>
33
+ </v-form>
34
+ </v-card-text>
35
+
36
+ <v-toolbar density="compact">
37
+ <v-row no-gutters>
38
+ <v-col cols="6">
39
+ <v-btn
40
+ tile
41
+ block
42
+ variant="text"
43
+ class="text-none"
44
+ size="48"
45
+ @click="cancel"
46
+ :disabled="disable"
47
+ >
48
+ Cancel
49
+ </v-btn>
50
+ </v-col>
51
+
52
+ <v-col cols="6">
53
+ <v-btn
54
+ tile
55
+ block
56
+ variant="flat"
57
+ color="black"
58
+ class="text-none"
59
+ size="48"
60
+ :disabled="!validForm || disable"
61
+ @click="submit"
62
+ :loading="disable"
63
+ >
64
+ Submit
65
+ </v-btn>
66
+ </v-col>
67
+ </v-row>
68
+ </v-toolbar>
69
+ </v-card>
70
+ </template>
71
+ <script setup lang="ts">
72
+ const prop = defineProps({
73
+ mode: {
74
+ type: String,
75
+ default: "add",
76
+ },
77
+ document: {
78
+ type: Object as PropType<TDocument>,
79
+ default: () => ({
80
+ name: "",
81
+ attachment: "",
82
+ }),
83
+ },
84
+ });
85
+
86
+ const emit = defineEmits(["cancel", "success"]);
87
+
88
+ const validForm = ref(false);
89
+ const disable = ref(false);
90
+ const message = ref("");
91
+
92
+ function cancel() {
93
+ // createMore.value = false;
94
+ message.value = "";
95
+ emit("cancel");
96
+ }
97
+
98
+ async function submit() {
99
+ disable.value = true;
100
+ }
101
+ </script>
@@ -0,0 +1,187 @@
1
+ <template>
2
+ <v-row no-gutters>
3
+ <v-col cols="12" class="mb-2">
4
+ <v-row no-gutters>
5
+ <v-btn
6
+ class="text-none"
7
+ rounded="pill"
8
+ variant="tonal"
9
+ size="large"
10
+ @click="setDocument()"
11
+ v-if="canCreate && canCreateDocument"
12
+ >
13
+ Add Document
14
+ </v-btn>
15
+ </v-row>
16
+ </v-col>
17
+ <v-col cols="12">
18
+ <v-card
19
+ width="100%"
20
+ variant="outlined"
21
+ border="thin"
22
+ rounded="lg"
23
+ :loading="loading"
24
+ >
25
+ <v-toolbar density="compact" color="grey-lighten-4">
26
+ <template #prepend>
27
+ <v-btn fab icon density="comfortable" @click="">
28
+ <v-icon>mdi-refresh</v-icon>
29
+ </v-btn>
30
+ </template>
31
+
32
+ <template #append>
33
+ <v-row no-gutters justify="end" align="center">
34
+ <span class="mr-2 text-caption text-fontgray">
35
+ {{ pageRange }}
36
+ </span>
37
+ <local-pagination
38
+ v-model="page"
39
+ :length="pages"
40
+ @update:value=""
41
+ />
42
+ </v-row>
43
+ </template>
44
+ </v-toolbar>
45
+ <v-data-table
46
+ :headers="headers"
47
+ :items="items"
48
+ item-value="_id"
49
+ items-per-page="10"
50
+ fixed-header
51
+ hide-default-footer
52
+ hide-default-header
53
+ @click:row="tableRowClickHandler"
54
+ style="max-height: calc(100vh - (200px))"
55
+ ></v-data-table>
56
+ </v-card>
57
+ </v-col>
58
+
59
+ <!-- Create Dialog -->
60
+ <v-dialog v-model="createDialog" width="450" persistent>
61
+ <DocumentForm @cancel="createDialog = false" @success="successCreate()" />
62
+ </v-dialog>
63
+ </v-row>
64
+ </template>
65
+ <script setup lang="ts">
66
+ definePageMeta({
67
+ middleware: ["01-auth", "02-org"],
68
+ memberOnly: true,
69
+ });
70
+ const props = defineProps({
71
+ headers: {
72
+ type: Array as PropType<Array<Record<string, any>>>,
73
+ default: () => [
74
+ {
75
+ title: "Name",
76
+ value: "name",
77
+ },
78
+ { title: "Action", value: "action-table" },
79
+ ],
80
+ },
81
+ canCreate: {
82
+ type: Boolean,
83
+ default: true,
84
+ },
85
+ canUpdate: {
86
+ type: Boolean,
87
+ default: true,
88
+ },
89
+ canDelete: {
90
+ type: Boolean,
91
+ default: true,
92
+ },
93
+ canCreateDocument: {
94
+ type: Boolean,
95
+ default: true,
96
+ },
97
+ canUpdateDocument: {
98
+ type: Boolean,
99
+ default: true,
100
+ },
101
+ canDeleteDocument: {
102
+ type: Boolean,
103
+ default: true,
104
+ },
105
+ });
106
+
107
+ const { headerSearch } = useLocal();
108
+ const { getAll: _getAllDocuments } = useDocument();
109
+
110
+ const page = ref(1);
111
+ const pages = ref(0);
112
+ const pageRange = ref("-- - -- of --");
113
+
114
+ const message = ref("");
115
+ const messageSnackbar = ref(false);
116
+ const messageColor = ref("");
117
+
118
+ const items = ref<Array<Record<string, any>>>([]);
119
+
120
+ const {
121
+ data: getDocumentReq,
122
+ refresh: getDocuments,
123
+ status: getAllReqStatus,
124
+ } = useLazyAsyncData(
125
+ "get-all-documents",
126
+ () =>
127
+ _getAllDocuments({
128
+ page: page.value,
129
+ search: headerSearch.value,
130
+ }),
131
+ {
132
+ watch: [page, headerSearch],
133
+ }
134
+ );
135
+
136
+ const loading = computed(() => getAllReqStatus.value === "pending");
137
+
138
+ watchEffect(() => {
139
+ if (getDocumentReq.value) {
140
+ items.value = getDocumentReq.value.items;
141
+ pages.value = getDocumentReq.value.pages;
142
+ pageRange.value = getDocumentReq.value.pageRange;
143
+ }
144
+ });
145
+
146
+ const createDialog = ref(false);
147
+ const editDialog = ref(false);
148
+ const previewDialog = ref(false);
149
+ const selectedDocument = ref<TDocument>({
150
+ _id: "",
151
+ name: "",
152
+ attachment: [],
153
+ });
154
+
155
+ function tableRowClickHandler(_: any, data: any) {
156
+ selectedDocument.value = data.item as TDocument;
157
+ previewDialog.value = true;
158
+ }
159
+
160
+ function setDocument({
161
+ mode = "create",
162
+ dialog = true,
163
+ data = {} as TDocument,
164
+ } = {}) {
165
+ if (mode === "create") {
166
+ createDialog.value = dialog;
167
+ } else if (mode === "edit") {
168
+ editDialog.value = dialog;
169
+ selectedDocument.value = data;
170
+ } else if (mode === "preview") {
171
+ previewDialog.value = dialog;
172
+ selectedDocument.value = data;
173
+ }
174
+ }
175
+
176
+ function showMessage(msg: string, color: string) {
177
+ message.value = msg;
178
+ messageColor.value = color;
179
+ messageSnackbar.value = true;
180
+ }
181
+
182
+ function successCreate() {
183
+ createDialog.value = false;
184
+ getDocuments();
185
+ showMessage("Document created successfully!", "success");
186
+ }
187
+ </script>
@@ -36,7 +36,7 @@
36
36
  >
37
37
  <v-toolbar density="compact" color="grey-lighten-4">
38
38
  <template #prepend>
39
- <v-btn fab icon density="comfortable" @click="updatePage">
39
+ <v-btn fab icon density="comfortable" @click="updatePage(1)">
40
40
  <v-icon>mdi-refresh</v-icon>
41
41
  </v-btn>
42
42
  </template>
@@ -351,6 +351,7 @@ async function updatePage(pageVal: any) {
351
351
  const response = await _getFeedbacks({
352
352
  page: page.value,
353
353
  site: route.params.site as string,
354
+ category: props.category,
354
355
  });
355
356
  if (response) {
356
357
  items.value = response.items;
@@ -3,13 +3,14 @@
3
3
  <v-col cols="12" class="d-flex ga-2">
4
4
  <v-select v-model="selectedCode" :variant="variant" :items="countries" item-title="code" item-value="code"
5
5
  hide-details class="px-0" :density="density" style="max-width: 95px" :rules="[...props.rules]"
6
- @update:model-value="handleUpdateCountry">
6
+ :readonly="props.readOnly" @update:model-value="handleUpdateCountry">
7
7
  <template v-slot:item="{ props: itemProps, item }">
8
8
  <v-list-item v-bind="itemProps" :title="item.raw.name" :subtitle="item.raw.dial_code" width="300" />
9
9
  </template>
10
10
  </v-select>
11
- <v-mask-input v-model="phone" :mask="currentMask" :rules="[...props.rules, validatePhone]" :loading="loading"
12
- :variant="variant" hint="Enter a valid phone number" hide-details persistent-hint return-masked-value
11
+ <v-mask-input v-model="input" :mask="currentMask" :rules="[...props.rules, validatePhone]" ref="maskRef" :key="`mask-key-${maskKey}`"
12
+ :loading="loading" :readonly="props.readOnly" :variant="variant" hint="Enter a valid phone number"
13
+ hide-details persistent-hint return-masked-value :prefix="phonePrefix || '###'" persistent-placeholder
13
14
  :density="density" :placeholder="placeholder || currentMask"></v-mask-input>
14
15
  </v-col>
15
16
  <span class="text-error text-caption w-100" v-if="errorMessage && !hideDetails">{{ errorMessage }}</span>
@@ -45,6 +46,10 @@ const props = defineProps({
45
46
  loading: {
46
47
  type: Boolean,
47
48
  default: false
49
+ },
50
+ readOnly: {
51
+ type: Boolean,
52
+ default: false
48
53
  }
49
54
  })
50
55
 
@@ -60,9 +65,12 @@ type TPhoneMask =
60
65
  }
61
66
 
62
67
  const phone = defineModel({ default: '' })
68
+ const input = ref('')
63
69
  const selectedCode = ref('SG')
64
70
  const countries = phoneMasks
65
71
  const errorMessage = ref('')
72
+ const maskRef = ref()
73
+ const maskKey = ref(0)
66
74
 
67
75
  const currentMask = computed(() => {
68
76
  const country = phoneMasks.find((c: TPhoneMask) => c.code === selectedCode.value)
@@ -72,7 +80,9 @@ const currentMask = computed(() => {
72
80
 
73
81
 
74
82
 
75
- const validatePhone = (value: string): boolean | string => {
83
+ const validatePhone = (): boolean | string => {
84
+ if(props.readOnly) return true;
85
+ const value = phone.value
76
86
  if (!value) {
77
87
  errorMessage.value = ''
78
88
  return true;
@@ -92,23 +102,29 @@ const validatePhone = (value: string): boolean | string => {
92
102
 
93
103
 
94
104
 
95
-
96
105
  function generateMaskFromRegex(regex: string): string {
106
+ let pattern = regex.replace(/^\^|\$$/g, '');
97
107
 
98
- let pattern = regex.replace(/^\^|\$$/g, '')
99
- pattern = pattern.replace(/\\d\{(\d+)\}/g, (_, count) => '#'.repeat(Number(count)))
100
- pattern = pattern.replace(/\\d/g, '#')
108
+ pattern = pattern.replace(/\(\?:\+?\d+\)\?/g, '');
109
+ pattern = pattern.replace(/\+?\d{1,4}/, '');
101
110
 
102
- pattern = pattern.replace(/\\/g, '')
111
+ pattern = pattern.replace(/\\d\{(\d+)\}/g, (_, count) => '#'.repeat(Number(count)));
103
112
 
104
- pattern = pattern.replace(/\s\?/g, ' ')
105
- pattern = pattern.replace(/\s/g, ' ')
113
+ pattern = pattern.replace(/\\d/g, '#');
106
114
 
107
- pattern = pattern.replace(/\(\?:/g, '(')
115
+ pattern = pattern.replace(/\\/g, '');
116
+ pattern = pattern.replace(/\(\?:/g, '');
117
+ pattern = pattern.trim();
108
118
 
109
- return pattern.trim()
119
+ return pattern;
110
120
  }
111
121
 
122
+ const phonePrefix = computed(() => {
123
+ const country = phoneMasks.find((c: TPhoneMask) => c.code === selectedCode.value)
124
+ return country?.dial_code || ''
125
+ })
126
+
127
+
112
128
  function handleUpdateCountry() {
113
129
  phone.value = ''
114
130
  }
@@ -116,8 +132,32 @@ function handleUpdateCountry() {
116
132
  const emit = defineEmits(['update:modelValue'])
117
133
 
118
134
  watch(phone, (newVal) => {
119
- emit('update:modelValue', newVal)
135
+ emit('update:modelValue', newVal)
136
+ })
137
+
138
+ watch(input, (newInput) => {
139
+ const prefix = phonePrefix.value
140
+
141
+ if (!newInput) {
142
+ return
143
+ }
144
+
145
+ phone.value = prefix + newInput
146
+ })
147
+
148
+
149
+ onMounted(() => {
150
+ if (!phone.value) return
151
+
152
+ const found = phoneMasks.find((c: any) => phone.value?.startsWith(c?.dial_code))
153
+ if (found) {
154
+ selectedCode.value = found.code
155
+ }
156
+
157
+ input.value = phone.value.replace(found?.dial_code || '', '')
158
+ maskKey.value++
120
159
  })
121
160
 
122
161
 
162
+
123
163
  </script>
@@ -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("-- - -- of --");
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>> = [
@@ -7,7 +7,7 @@
7
7
  rounded="pill"
8
8
  variant="tonal"
9
9
  size="large"
10
- @click="showCreateDialog = true"
10
+ @click="(showCreateDialog = true), (isEditMode = false)"
11
11
  v-if="canCreateWorkOrder"
12
12
  >
13
13
  Create Work Order
@@ -35,7 +35,7 @@
35
35
  >
36
36
  <v-toolbar density="compact" color="grey-lighten-4">
37
37
  <template #prepend>
38
- <v-btn fab icon density="comfortable" @click="updatePage">
38
+ <v-btn fab icon density="comfortable" @click="updatePage(1)">
39
39
  <v-icon>mdi-refresh</v-icon>
40
40
  </v-btn>
41
41
  </template>
@@ -258,7 +258,12 @@ const selected = ref<string[]>([]);
258
258
  const route = useRoute();
259
259
  const { customers } = useCustomer();
260
260
 
261
- const { getWorkOrders: _getWorkOrders, createWorkOrder } = useWorkOrder();
261
+ const {
262
+ getWorkOrders: _getWorkOrders,
263
+ createWorkOrder,
264
+ getWorkOrderById,
265
+ updateWorkOrder,
266
+ } = useWorkOrder();
262
267
 
263
268
  const page = ref(1);
264
269
  const pages = ref(0);
@@ -294,6 +299,7 @@ async function updatePage(pageVal: any) {
294
299
  const response = await _getWorkOrders({
295
300
  page: page.value,
296
301
  site: route.params.site as string,
302
+ category: props.category,
297
303
  });
298
304
  if (response) {
299
305
  items.value = response.items;
@@ -412,7 +418,13 @@ async function submitWorkOrder() {
412
418
  site: route.params.site as string,
413
419
  };
414
420
 
415
- const res = await createWorkOrder(payload);
421
+ let res: Record<string, any> = {};
422
+
423
+ if (isEditMode.value) {
424
+ res = await updateWorkOrder(_workOrderId.value as string, payload);
425
+ } else {
426
+ res = await createWorkOrder(payload);
427
+ }
416
428
 
417
429
  showMessage(res.message, "success");
418
430
  showCreateDialog.value = false;
@@ -437,12 +449,32 @@ function onViewWorkOrder(item: any) {
437
449
  });
438
450
  }
439
451
 
440
- function editWorkOrder(item: any) {}
452
+ async function editWorkOrder(item: any) {
453
+ try {
454
+ const _workOrders = await getWorkOrderById(item._id);
455
+ console.log(_workOrders);
456
+ _workOrder.value = {
457
+ attachments: (_workOrders.attachments || []) as string[],
458
+ category: _workOrders.category || "",
459
+ subject: _workOrders.subject || "",
460
+ description: _workOrders.description || "",
461
+ highPriority: _workOrders.highPriority || false,
462
+ unit: _workOrders.location || "",
463
+ };
464
+ _workOrderId.value = item._id;
465
+ isEditMode.value = true;
466
+ showCreateDialog.value = true;
467
+ dialogPreview.value = false;
468
+ } catch (error) {
469
+ showMessage("Failed to load full work order", "error");
470
+ }
471
+ }
472
+
473
+ async function _updateWorkOrder() {}
441
474
 
442
475
  function confirmDeleteWorkOrder(item: any) {}
443
476
 
444
477
  function tableRowClickHandler(_: any, data: any) {
445
- console.log(data.item);
446
478
  selectedWorkOrder.value = data.item;
447
479
  dialogPreview.value = true;
448
480
  }