@7365admin1/layer-common 1.8.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.
Files changed (198) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.editorconfig +12 -0
  4. package/.github/workflows/main.yml +17 -0
  5. package/.github/workflows/publish.yml +39 -0
  6. package/.nuxtrc +1 -0
  7. package/.playground/app.vue +41 -0
  8. package/.playground/eslint.config.mjs +6 -0
  9. package/.playground/nuxt.config.ts +22 -0
  10. package/.playground/pages/feedback.vue +30 -0
  11. package/CHANGELOG.md +263 -0
  12. package/README.md +73 -0
  13. package/app.vue +3 -0
  14. package/components/AccessCardAddForm.vue +363 -0
  15. package/components/AccessManagement.vue +420 -0
  16. package/components/Avatar/Main.vue +68 -0
  17. package/components/BillingMain.vue +66 -0
  18. package/components/BtnUploadFile.vue +139 -0
  19. package/components/BuildingForm.vue +303 -0
  20. package/components/BuildingManagement/buildings.vue +335 -0
  21. package/components/BuildingManagement/units.vue +350 -0
  22. package/components/BuildingUnitFormAdd.vue +441 -0
  23. package/components/BuildingUnitFormEdit.vue +429 -0
  24. package/components/CameraForm.vue +264 -0
  25. package/components/CameraMain.vue +352 -0
  26. package/components/Card/DeleteConfirmation.vue +51 -0
  27. package/components/Card/MemberInfoSummary.vue +44 -0
  28. package/components/Card/Toggle.vue +25 -0
  29. package/components/Chat/Bubbles.vue +53 -0
  30. package/components/Chat/Information.vue +416 -0
  31. package/components/Chat/ListCard.vue +62 -0
  32. package/components/Chat/Message.vue +158 -0
  33. package/components/Chat/Navigation.vue +150 -0
  34. package/components/ConfirmDialog.vue +66 -0
  35. package/components/Container/Standard.vue +33 -0
  36. package/components/DashboardPlaceholder.vue +1524 -0
  37. package/components/Dialog/DeleteConfirmation.vue +51 -0
  38. package/components/Dialog/ReplaceAutofillPrompt.vue +49 -0
  39. package/components/Dialog/UpdateMoreAction.vue +103 -0
  40. package/components/DocumentForm.vue +187 -0
  41. package/components/DocumentManagement.vue +376 -0
  42. package/components/Editor.vue +95 -0
  43. package/components/EntryPassMain.vue +518 -0
  44. package/components/Feedback/Form.vue +173 -0
  45. package/components/FeedbackDetail.vue +599 -0
  46. package/components/FeedbackMain.vue +588 -0
  47. package/components/FormDialog.vue +65 -0
  48. package/components/ImageCarousel.vue +138 -0
  49. package/components/Input/Date.vue +177 -0
  50. package/components/Input/DateTimePicker.vue +131 -0
  51. package/components/Input/File.vue +236 -0
  52. package/components/Input/FileV2.vue +234 -0
  53. package/components/Input/InputPhoneNumberV2.vue +164 -0
  54. package/components/Input/ListGroupSelection.vue +96 -0
  55. package/components/Input/NRICNumber.vue +53 -0
  56. package/components/Input/NewDate.vue +123 -0
  57. package/components/Input/Number.vue +124 -0
  58. package/components/Input/Password.vue +22 -0
  59. package/components/Input/PhoneNumber.vue +188 -0
  60. package/components/Input/VehicleNumber.vue +49 -0
  61. package/components/InputLabel.vue +22 -0
  62. package/components/InvitationForm.vue +359 -0
  63. package/components/InvitationMain.vue +310 -0
  64. package/components/Layout/Header.vue +129 -0
  65. package/components/Layout/NavigationDrawer.vue +44 -0
  66. package/components/ListItem.vue +35 -0
  67. package/components/ListView.vue +87 -0
  68. package/components/LocalPagination.vue +31 -0
  69. package/components/MemberMain.vue +459 -0
  70. package/components/NFC/NFCPatrolReportMain.vue +591 -0
  71. package/components/NFC/NFCPatrolRouteForm.vue +596 -0
  72. package/components/NFC/NFCPatrolRouteMain.vue +539 -0
  73. package/components/NFC/NFCTagForm.vue +236 -0
  74. package/components/NFC/NFCTagMain.vue +337 -0
  75. package/components/NFC/PatrolSettings.vue +130 -0
  76. package/components/NavigationItem.vue +83 -0
  77. package/components/NumberSettingField.vue +107 -0
  78. package/components/OnlineFormConfigurationForm.vue +290 -0
  79. package/components/OnlineFormsConfiguration.vue +429 -0
  80. package/components/PeopleForm.vue +452 -0
  81. package/components/PlaceholderComponent.vue +34 -0
  82. package/components/RolePermissionFormCreate.vue +161 -0
  83. package/components/RolePermissionFormPreviewUpdate.vue +183 -0
  84. package/components/RolePermissionMain.vue +361 -0
  85. package/components/SearchVehicleNumberUser.vue +91 -0
  86. package/components/ServiceProviderFormCreate.vue +154 -0
  87. package/components/ServiceProviderMain.vue +547 -0
  88. package/components/SignaturePad.vue +73 -0
  89. package/components/Snackbar.vue +23 -0
  90. package/components/SpecificAttr.vue +53 -0
  91. package/components/SupplyManagement.vue +292 -0
  92. package/components/SwitchContext.vue +108 -0
  93. package/components/TableList.vue +150 -0
  94. package/components/TableListSecondary.vue +164 -0
  95. package/components/TableMain.vue +142 -0
  96. package/components/TableWithButton.vue +94 -0
  97. package/components/VehicleUpdateMoreAction.vue +84 -0
  98. package/components/VideoPlayer.vue +125 -0
  99. package/components/VisitorForm.vue +659 -0
  100. package/components/VisitorFormSelection.vue +53 -0
  101. package/components/VisitorManagement.vue +490 -0
  102. package/components/WorkOrder/Create.vue +284 -0
  103. package/components/WorkOrder/Detail.vue +71 -0
  104. package/components/WorkOrder/ListView.vue +96 -0
  105. package/components/WorkOrder/Main.vue +489 -0
  106. package/components/Workorder.vue +1 -0
  107. package/composables/useAddress.ts +107 -0
  108. package/composables/useBuilding.ts +250 -0
  109. package/composables/useBuildingUnit.ts +116 -0
  110. package/composables/useCard.ts +46 -0
  111. package/composables/useCommonPermission.ts +207 -0
  112. package/composables/useCustomer.ts +113 -0
  113. package/composables/useCustomerSite.ts +56 -0
  114. package/composables/useDashboard.ts +31 -0
  115. package/composables/useDashboardData.ts +425 -0
  116. package/composables/useDocument.ts +57 -0
  117. package/composables/useFacility.ts +246 -0
  118. package/composables/useFeedback.ts +119 -0
  119. package/composables/useFile.ts +55 -0
  120. package/composables/useInvoice.ts +18 -0
  121. package/composables/useLocal.ts +131 -0
  122. package/composables/useLocalAuth.ts +137 -0
  123. package/composables/useLocalSetup.ts +13 -0
  124. package/composables/useMember.ts +111 -0
  125. package/composables/useNFCPatrolRoute.ts +77 -0
  126. package/composables/useNFCPatrolSettings.ts +19 -0
  127. package/composables/useNFCPatrolTag.ts +53 -0
  128. package/composables/useOnlineForm.ts +67 -0
  129. package/composables/useOrg.ts +129 -0
  130. package/composables/usePDFDownload.ts +25 -0
  131. package/composables/usePaymentMethod.ts +101 -0
  132. package/composables/usePeople.ts +81 -0
  133. package/composables/usePermission.ts +54 -0
  134. package/composables/usePhoneCountries.ts +561 -0
  135. package/composables/usePrice.ts +15 -0
  136. package/composables/usePromoCode.ts +36 -0
  137. package/composables/useRecapPermission.ts +26 -0
  138. package/composables/useRole.ts +104 -0
  139. package/composables/useSecurityUtils.ts +18 -0
  140. package/composables/useServiceProvider.ts +224 -0
  141. package/composables/useSite.ts +109 -0
  142. package/composables/useSiteEntryPassSettings.ts +46 -0
  143. package/composables/useSiteSettings.ts +123 -0
  144. package/composables/useSubscription.ts +150 -0
  145. package/composables/useUser.ts +132 -0
  146. package/composables/useUtils.ts +445 -0
  147. package/composables/useVerification.ts +34 -0
  148. package/composables/useVisitor.ts +120 -0
  149. package/composables/useWorkOrder.ts +85 -0
  150. package/error.vue +41 -0
  151. package/layouts/plain.vue +7 -0
  152. package/middleware/01.auth.ts +20 -0
  153. package/middleware/02.org.ts +21 -0
  154. package/middleware/03.customer.ts +13 -0
  155. package/middleware/member.ts +4 -0
  156. package/nuxt.config.ts +54 -0
  157. package/package.json +39 -0
  158. package/pages/index.vue +3 -0
  159. package/pages/payment-method-linked.vue +31 -0
  160. package/pages/require-customer.vue +56 -0
  161. package/pages/require-organization-membership.vue +47 -0
  162. package/pages/unauthorized.vue +29 -0
  163. package/plugins/API.ts +21 -0
  164. package/plugins/iconify.client.ts +5 -0
  165. package/plugins/secure-member.client.ts +86 -0
  166. package/plugins/vuetify.ts +62 -0
  167. package/public/bg-camera.jpg +0 -0
  168. package/public/bg-city.jpg +0 -0
  169. package/public/bg-condo.jpg +0 -0
  170. package/public/images/icons/delete-icon.png +0 -0
  171. package/public/sprite.svg +1 -0
  172. package/tsconfig.json +3 -0
  173. package/types/address.d.ts +13 -0
  174. package/types/building.d.ts +27 -0
  175. package/types/camera.d.ts +31 -0
  176. package/types/card.d.ts +22 -0
  177. package/types/customer.d.ts +27 -0
  178. package/types/document.d.ts +6 -0
  179. package/types/feedback.d.ts +68 -0
  180. package/types/local.d.ts +74 -0
  181. package/types/member.d.ts +21 -0
  182. package/types/online-form.d.ts +15 -0
  183. package/types/org.d.ts +13 -0
  184. package/types/people.d.ts +24 -0
  185. package/types/permission.d.ts +25 -0
  186. package/types/phone-number.d.ts +10 -0
  187. package/types/price.d.ts +17 -0
  188. package/types/promo-code.d.ts +19 -0
  189. package/types/role.d.ts +11 -0
  190. package/types/select.d.ts +4 -0
  191. package/types/service-provider.d.ts +15 -0
  192. package/types/site.d.ts +20 -0
  193. package/types/subscription.d.ts +23 -0
  194. package/types/user.d.ts +19 -0
  195. package/types/verification.d.ts +20 -0
  196. package/types/visitor.d.ts +42 -0
  197. package/types/work-order.d.ts +42 -0
  198. package/utils/phoneMasks.ts +1703 -0
@@ -0,0 +1,73 @@
1
+ <template>
2
+ <div>
3
+ <Vue3Signature
4
+ ref="signatureRef"
5
+ :sigOption="options"
6
+ @save="onSave"
7
+ style="border: 1px solid #ccc; width: 100%; height: 200px"
8
+ />
9
+ <div class="mt-4 flex justify-end">
10
+ <button @click="clear" class="px-4 py-2 bg-red-500 rounded">Clear</button>
11
+ <button @click="save" class="px-4 py-2 bg-green-500 rounded ml-2">
12
+ Save
13
+ </button>
14
+ </div>
15
+
16
+ <div style="position: fixed; top: 20px; right: 20px; z-index: 9999">
17
+ <Snackbar
18
+ v-model="messageSnackbar"
19
+ :text="message"
20
+ :color="messageColor"
21
+ />
22
+ </div>
23
+ </div>
24
+ </template>
25
+
26
+ <script setup lang="ts">
27
+ import { ref, defineProps, defineEmits } from "vue";
28
+ import Vue3Signature from "vue3-signature";
29
+
30
+ defineProps({
31
+ modelValue: String,
32
+ });
33
+
34
+ const emit = defineEmits(["update:modelValue"]);
35
+
36
+ const signatureRef = ref();
37
+
38
+ const options = {
39
+ penColor: "black",
40
+ backgroundColor: "white",
41
+ };
42
+
43
+ // Snackbar control
44
+ const message = ref("");
45
+ const messageColor = ref("");
46
+ const messageSnackbar = ref(false);
47
+
48
+ function showMessage(msg: string, color: string) {
49
+ message.value = msg;
50
+ messageColor.value = color;
51
+ messageSnackbar.value = true;
52
+ }
53
+
54
+ function onSave(data: string) {
55
+ emit("update:modelValue", data);
56
+ showMessage("Signature saved successfully.", "success");
57
+ }
58
+
59
+ function save() {
60
+ const signatureData = signatureRef.value?.save("image/jpeg");
61
+ if (signatureData) {
62
+ emit("update:modelValue", signatureData);
63
+ showMessage("Signature saved successfully.", "success");
64
+ } else {
65
+ showMessage("No signature to save.", "error");
66
+ }
67
+ }
68
+
69
+ function clear() {
70
+ signatureRef.value?.clear();
71
+ showMessage("Signature cleared.", "success");
72
+ }
73
+ </script>
@@ -0,0 +1,23 @@
1
+ <template>
2
+ <v-snackbar v-model="snackbar" :color="props.color" :z-index="100000000" >
3
+ {{ props.text }}
4
+
5
+ <template #actions>
6
+ <v-btn variant="text" @click="snackbar = false"> Close </v-btn>
7
+ </template>
8
+ </v-snackbar>
9
+ </template>
10
+
11
+ <script setup lang="ts">
12
+ const snackbar = defineModel({ type: Boolean });
13
+ const props = defineProps({
14
+ text: {
15
+ type: String,
16
+ default: "",
17
+ },
18
+ color: {
19
+ type: String,
20
+ default: "primary",
21
+ },
22
+ });
23
+ </script>
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <v-row no-gutters class="pa-4" justify="center">
3
+ <v-col cols="12" :lg="props.lg" :md="props.md" :sm="props.sm">
4
+ <v-row no-gutters>
5
+ <v-col cols="12">
6
+ <span
7
+ class="font-weight-medium text-subtitle-1 text-decoration-underline cursor-pointer"
8
+ @click="emit('back')"
9
+ >
10
+ Back
11
+ </span>
12
+ </v-col>
13
+ <v-col cols="12">
14
+ <span class="text-h5">{{ props.title }}</span>
15
+ </v-col>
16
+
17
+ <v-col cols="12" class="mt-4">
18
+ <slot name="default"> </slot>
19
+ </v-col>
20
+
21
+ <v-col cols="12" class="mt-4">
22
+ <slot name="footer"> </slot>
23
+ </v-col>
24
+ </v-row>
25
+ </v-col>
26
+ </v-row>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ const emit = defineEmits(["back"]);
31
+ const props = defineProps({
32
+ title: {
33
+ type: String,
34
+ default: "Title",
35
+ },
36
+ lg: {
37
+ type: String,
38
+ default: "4",
39
+ },
40
+ md: {
41
+ type: String,
42
+ default: "6",
43
+ },
44
+ sm: {
45
+ type: String,
46
+ default: "6",
47
+ },
48
+ fluid: {
49
+ type: Boolean,
50
+ default: false,
51
+ },
52
+ });
53
+ </script>
@@ -0,0 +1,292 @@
1
+ <template>
2
+ <v-row no-gutters>
3
+ <v-col cols="12" lg="12">
4
+ <TableMain
5
+ :title="`Units`"
6
+ :items="items"
7
+ :headers="headers"
8
+ :loading="loading"
9
+ :show-header="true"
10
+ v-model:page="page"
11
+ :pages="pages"
12
+ :pageRange="pageRange"
13
+ :no-data-text="`No supplies found.`"
14
+ @refresh="updatePage"
15
+ :canCreate="true"
16
+ createLabel="Add Supply"
17
+ @row-click="tableRowClickHandler"
18
+ >
19
+ <template #actions>
20
+ <v-row no-gutters align="center" class="w-100">
21
+ <v-col cols="auto">
22
+ <v-btn
23
+ class="text-none"
24
+ rounded="pill"
25
+ variant="tonal"
26
+ size="large"
27
+ >
28
+ Add Supply
29
+ </v-btn>
30
+ </v-col>
31
+
32
+ <v-spacer />
33
+
34
+ <v-col cols="auto">
35
+ <v-text-field
36
+ v-model="searchText"
37
+ density="compact"
38
+ placeholder="Search"
39
+ clearable
40
+ width="300"
41
+ append-inner-icon="mdi-magnify"
42
+ hide-details
43
+ />
44
+ </v-col>
45
+ </v-row>
46
+ </template>
47
+
48
+ <template #item.block="{ value }">
49
+ {{ value ? `blk ${value}` : "" }}
50
+ </template>
51
+ </TableMain>
52
+ </v-col>
53
+ </v-row>
54
+ </template>
55
+ <script setup lang="ts">
56
+ const searchText = ref("");
57
+ const loading = ref(false);
58
+
59
+ const page = ref(1);
60
+ const pages = ref(0);
61
+ const pageRange = ref("1-10 of 10");
62
+ const items = ref<Array<Record<string, any>>>([]);
63
+
64
+ const headers: Array<Record<string, any>> = [
65
+ { title: "Item", value: "item", align: "start" },
66
+ // { title: "Type", value: "type", align: "start" },
67
+ // { title: "Created By", value: "createdByName", align: "start" },
68
+ // { title: "Last Updated", value: "lastUpdated", align: "start" },
69
+ // { title: "Available Qty", value: "availableQty", align: "start" },
70
+ { title: "Stock Qty", value: "stockQty", align: "start" },
71
+ { title: "", value: "action-table", align: "end" },
72
+ ];
73
+
74
+ watchEffect(() => {
75
+ items.value = [
76
+ {
77
+ item: "Multi-Surface Cleaner",
78
+ type: "Consumable",
79
+ createdByName: "John Reyes",
80
+ lastUpdated: "2025-11-20",
81
+ availableQty: 45,
82
+ stockQty: 100,
83
+ },
84
+ {
85
+ item: "Disinfectant Spray",
86
+ type: "Consumable",
87
+ createdByName: "Marianne Santos",
88
+ lastUpdated: "2025-11-18",
89
+ availableQty: 30,
90
+ stockQty: 80,
91
+ },
92
+ {
93
+ item: "Glass Cleaner",
94
+ type: "Consumable",
95
+ createdByName: "Evan Lim",
96
+ lastUpdated: "2025-11-22",
97
+ availableQty: 60,
98
+ stockQty: 120,
99
+ },
100
+ {
101
+ item: "Floor Degreaser",
102
+ type: "Consumable",
103
+ createdByName: "Kristine Tan",
104
+ lastUpdated: "2025-11-21",
105
+ availableQty: 20,
106
+ stockQty: 50,
107
+ },
108
+ {
109
+ item: "Microfiber Cloth",
110
+ type: "Equipment",
111
+ createdByName: "Angela Chua",
112
+ lastUpdated: "2025-11-22",
113
+ availableQty: 120,
114
+ stockQty: 200,
115
+ },
116
+ {
117
+ item: "Toilet Brush",
118
+ type: "Equipment",
119
+ createdByName: "Miguel Perez",
120
+ lastUpdated: "2025-11-19",
121
+ availableQty: 15,
122
+ stockQty: 40,
123
+ },
124
+ {
125
+ item: "Mop Head Refill",
126
+ type: "Consumable",
127
+ createdByName: "Sarah Ong",
128
+ lastUpdated: "2025-11-23",
129
+ availableQty: 50,
130
+ stockQty: 100,
131
+ },
132
+ {
133
+ item: "Cleaning Mop Set",
134
+ type: "Equipment",
135
+ createdByName: "Jenny Ramos",
136
+ lastUpdated: "2025-11-21",
137
+ availableQty: 10,
138
+ stockQty: 25,
139
+ },
140
+ {
141
+ item: "Trash Bags (Large)",
142
+ type: "Consumable",
143
+ createdByName: "Philip Cruz",
144
+ lastUpdated: "2025-11-18",
145
+ availableQty: 90,
146
+ stockQty: 150,
147
+ },
148
+ {
149
+ item: "Air Freshener Spray",
150
+ type: "Consumable",
151
+ createdByName: "Leo Dizon",
152
+ lastUpdated: "2025-11-20",
153
+ availableQty: 75,
154
+ stockQty: 150,
155
+ },
156
+ ];
157
+ pages.value = 2;
158
+ });
159
+
160
+ async function updatePage(pageVal: any) {
161
+ if (pageVal == 2) {
162
+ items.value = [
163
+ {
164
+ item: "Vacuum Cleaner",
165
+ type: "Equipment",
166
+ createdByName: "Robert Sy",
167
+ lastUpdated: "2025-11-22",
168
+ availableQty: 4,
169
+ stockQty: 10,
170
+ },
171
+ {
172
+ item: "Dishwashing Liquid",
173
+ type: "Consumable",
174
+ createdByName: "Kristine Tan",
175
+ lastUpdated: "2025-11-19",
176
+ availableQty: 55,
177
+ stockQty: 110,
178
+ },
179
+ {
180
+ item: "Pressure Washer",
181
+ type: "Equipment",
182
+ createdByName: "Marcel Tan",
183
+ lastUpdated: "2025-11-24",
184
+ availableQty: 2,
185
+ stockQty: 5,
186
+ },
187
+ {
188
+ item: "Scrubbing Pads",
189
+ type: "Consumable",
190
+ createdByName: "Hannah Cruz",
191
+ lastUpdated: "2025-11-23",
192
+ availableQty: 80,
193
+ stockQty: 150,
194
+ },
195
+ {
196
+ item: "Squeegee Set",
197
+ type: "Equipment",
198
+ createdByName: "Darryl Go",
199
+ lastUpdated: "2025-11-22",
200
+ availableQty: 12,
201
+ stockQty: 30,
202
+ },
203
+ ];
204
+ } else {
205
+ items.value = [
206
+ {
207
+ item: "Multi-Surface Cleaner",
208
+ type: "Consumable",
209
+ createdByName: "John Reyes",
210
+ lastUpdated: "2025-11-20",
211
+ availableQty: 45,
212
+ stockQty: 100,
213
+ },
214
+ {
215
+ item: "Disinfectant Spray",
216
+ type: "Consumable",
217
+ createdByName: "Marianne Santos",
218
+ lastUpdated: "2025-11-18",
219
+ availableQty: 30,
220
+ stockQty: 80,
221
+ },
222
+ {
223
+ item: "Glass Cleaner",
224
+ type: "Consumable",
225
+ createdByName: "Evan Lim",
226
+ lastUpdated: "2025-11-22",
227
+ availableQty: 60,
228
+ stockQty: 120,
229
+ },
230
+ {
231
+ item: "Floor Degreaser",
232
+ type: "Consumable",
233
+ createdByName: "Kristine Tan",
234
+ lastUpdated: "2025-11-21",
235
+ availableQty: 20,
236
+ stockQty: 50,
237
+ },
238
+ {
239
+ item: "Microfiber Cloth",
240
+ type: "Equipment",
241
+ createdByName: "Angela Chua",
242
+ lastUpdated: "2025-11-22",
243
+ availableQty: 120,
244
+ stockQty: 200,
245
+ },
246
+ {
247
+ item: "Toilet Brush",
248
+ type: "Equipment",
249
+ createdByName: "Miguel Perez",
250
+ lastUpdated: "2025-11-19",
251
+ availableQty: 15,
252
+ stockQty: 40,
253
+ },
254
+ {
255
+ item: "Mop Head Refill",
256
+ type: "Consumable",
257
+ createdByName: "Sarah Ong",
258
+ lastUpdated: "2025-11-23",
259
+ availableQty: 50,
260
+ stockQty: 100,
261
+ },
262
+ {
263
+ item: "Cleaning Mop Set",
264
+ type: "Equipment",
265
+ createdByName: "Jenny Ramos",
266
+ lastUpdated: "2025-11-21",
267
+ availableQty: 10,
268
+ stockQty: 25,
269
+ },
270
+ {
271
+ item: "Trash Bags (Large)",
272
+ type: "Consumable",
273
+ createdByName: "Philip Cruz",
274
+ lastUpdated: "2025-11-18",
275
+ availableQty: 90,
276
+ stockQty: 150,
277
+ },
278
+ {
279
+ item: "Air Freshener Spray",
280
+ type: "Consumable",
281
+ createdByName: "Leo Dizon",
282
+ lastUpdated: "2025-11-20",
283
+ availableQty: 75,
284
+ stockQty: 150,
285
+ },
286
+ ];
287
+ }
288
+ page.value = pageVal;
289
+ }
290
+
291
+ function tableRowClickHandler(_: any, data: any) {}
292
+ </script>
@@ -0,0 +1,108 @@
1
+ <template>
2
+ <v-menu
3
+ v-model="menu"
4
+ location="bottom"
5
+ offset="5px"
6
+ :close-on-content-click="false"
7
+ >
8
+ <template #activator="{ props }">
9
+ <v-list class="pa-0">
10
+ <v-list-item
11
+ v-bind="props"
12
+ :key="props.title"
13
+ :prepend-icon="prop.icon"
14
+ append-icon="mdi-chevron-down"
15
+ :to="props.route"
16
+ class="text-subtitle-2 font-weight-bold"
17
+ >
18
+ <v-list-item-title class="text-truncate">
19
+ {{ name || `Select ${prop.title}` }}
20
+ </v-list-item-title>
21
+ </v-list-item>
22
+ </v-list>
23
+ </template>
24
+
25
+ <v-card width="300px" rounded="lg" class="pa-4">
26
+ <span class="text-subtitle-2 font-weight-bold">
27
+ Switch {{ prop.title }} context
28
+ </span>
29
+ <v-text-field
30
+ v-model="search"
31
+ @keydown.enter="emit('search')"
32
+ density="compact"
33
+ width="100%"
34
+ hide-details
35
+ class="mb-2"
36
+ >
37
+ <template #prepend-inner>
38
+ <v-icon>mdi-magnify</v-icon>
39
+ </template>
40
+ </v-text-field>
41
+ <v-divider />
42
+ <v-list class="pa-0 my-2" density="compact" max-height="200px">
43
+ <v-list-item
44
+ density="compact"
45
+ class="text-caption font-weight-bold"
46
+ v-for="item in prop.items"
47
+ :key="item.value"
48
+ @click="selectItem(item.value)"
49
+ >
50
+ <v-icon size="16" class="mr-2">
51
+ {{ selected === item.value ? "mdi-check" : "" }}
52
+ </v-icon>
53
+
54
+ {{ item.text }}
55
+ </v-list-item>
56
+ </v-list>
57
+
58
+ <slot name="footer"></slot>
59
+ </v-card>
60
+ </v-menu>
61
+ </template>
62
+
63
+ <script setup lang="ts">
64
+ const prop = defineProps({
65
+ title: {
66
+ type: String,
67
+ required: true,
68
+ default: "app",
69
+ },
70
+ items: {
71
+ type: Array as PropType<Array<Record<string, any>>>,
72
+ required: false,
73
+ default: () => [],
74
+ },
75
+ icon: {
76
+ type: String,
77
+ required: false,
78
+ default: "mdi-domain",
79
+ },
80
+ logo: {
81
+ type: String,
82
+ required: false,
83
+ default: "",
84
+ },
85
+ });
86
+
87
+ const menu = defineModel<boolean>("menu", { required: false, default: false });
88
+ const selected = defineModel<string>("selected", {
89
+ required: false,
90
+ default: "",
91
+ });
92
+ const search = defineModel<string>("search", {
93
+ required: false,
94
+ default: "",
95
+ });
96
+
97
+ const emit = defineEmits(["search", "select"]);
98
+
99
+ const name = computed(() => {
100
+ return prop.items.find((item) => item.value === selected.value)?.text ?? "";
101
+ });
102
+
103
+ const selectItem = (value: string) => {
104
+ selected.value = value;
105
+ menu.value = false;
106
+ emit("select", value);
107
+ };
108
+ </script>
@@ -0,0 +1,150 @@
1
+ <template>
2
+ <v-card
3
+ width="100%"
4
+ variant="outlined"
5
+ border="thin"
6
+ rounded="lg"
7
+ :loading="loading"
8
+ >
9
+ <v-toolbar density="compact" color="grey-lighten-4">
10
+ <template #prepend>
11
+ <v-checkbox v-model="selectAll" hide-details class="mx-1"></v-checkbox>
12
+
13
+ <v-btn fab icon density="comfortable" @click="emit('refresh')">
14
+ <v-icon>mdi-refresh</v-icon>
15
+ </v-btn>
16
+ </template>
17
+
18
+ <template #append>
19
+ <v-row no-gutters justify="end" align="center">
20
+ <span class="mr-2 text-caption text-fontgray">
21
+ {{ props.pageRange }}
22
+ </span>
23
+ <div class="arrow-navigation">
24
+ <v-btn
25
+ icon="mdi-chevron-left"
26
+ variant="text"
27
+ density="comfortable"
28
+ :disabled="page <= 1"
29
+ @click="decrement"
30
+ />
31
+ <v-btn
32
+ icon="mdi-chevron-right"
33
+ variant="text"
34
+ density="comfortable"
35
+ :disabled="page >= props.length"
36
+ @click="increment"
37
+ />
38
+ </div>
39
+ <slot name="action-button"></slot>
40
+ </v-row>
41
+ </template>
42
+
43
+ <template v-if="$slots.extension" v-slot:extension>
44
+ <slot name="extension"></slot>
45
+ </template>
46
+ </v-toolbar>
47
+ <div :style="{ overflowY: 'auto', height }">
48
+ <v-data-table
49
+ v-model="selected"
50
+ :headers="props.headers"
51
+ :items="props.items"
52
+ item-value="_id"
53
+ items-per-page="20"
54
+ show-select
55
+ fixed-header
56
+ hide-default-footer
57
+ hide-default-header
58
+ :max-height="props.height"
59
+ :loading="props.loading"
60
+ @click:row="tableRowClickHandler"
61
+ >
62
+ <template v-if="$slots.item" v-slot:item>
63
+ <slot name="item" :items="props.items"></slot>
64
+ </template>
65
+
66
+ <template v-slot:item="{ item }">
67
+ <tr>
68
+ <td v-for="col in props.headers" :key="col.value">
69
+ <slot :name="col.value" :item="item">
70
+ {{ item[col.value] }}
71
+ </slot>
72
+ </td>
73
+ </tr>
74
+ </template>
75
+ </v-data-table>
76
+ </div>
77
+ </v-card>
78
+ </template>
79
+
80
+ <script setup lang="ts">
81
+ const selected = defineModel({
82
+ type: Array as PropType<Array<string>>,
83
+ default: () => [],
84
+ });
85
+ const page = defineModel("page", { type: Number, default: 0 });
86
+
87
+ const props = defineProps({
88
+ headers: {
89
+ type: Array as PropType<Array<Record<string, string>>>,
90
+ required: true,
91
+ default: () => [],
92
+ },
93
+ items: {
94
+ type: Array as PropType<Array<Record<string, any>>>,
95
+ required: true,
96
+ default: () => [],
97
+ },
98
+ pages: {
99
+ type: Number,
100
+ required: true,
101
+ default: 0,
102
+ },
103
+ pageRange: {
104
+ type: String,
105
+ required: true,
106
+ default: "-- - -- of --",
107
+ },
108
+ length: {
109
+ type: Number,
110
+ required: true,
111
+ default: 0,
112
+ },
113
+ loading: {
114
+ type: Boolean,
115
+ required: true,
116
+ default: true,
117
+ },
118
+ height: {
119
+ type: String,
120
+ default: "500px",
121
+ },
122
+ });
123
+
124
+ const emit = defineEmits(["refresh", "update:pagination", "row-click"]);
125
+
126
+ function increment() {
127
+ page.value++;
128
+ emit("update:pagination", page.value);
129
+ }
130
+
131
+ function decrement() {
132
+ page.value--;
133
+ emit("update:pagination", page.value);
134
+ }
135
+
136
+ function tableRowClickHandler(_: any, row: any) {
137
+ const item = props.items[row.index];
138
+ emit("row-click", item);
139
+ }
140
+
141
+ const selectAll = ref(false);
142
+
143
+ watch(selectAll, (curr) => {
144
+ selected.value.splice(0, selected.value.length);
145
+ if (curr) {
146
+ const ids = props.items.map((i) => i._id as string);
147
+ selected.value.push(...ids);
148
+ }
149
+ });
150
+ </script>