@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,445 @@
1
+ export default function useUtils() {
2
+ const requiredRule = (v: string) => !!v || "Required";
3
+
4
+ const emailRule = (v: string): true | string => {
5
+ const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
6
+ return regex.test(v) || "Please enter a valid email address";
7
+ };
8
+
9
+ const validateDate = (value: string): boolean | string => {
10
+ const dateRegex = /^(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/\d{4}$/;
11
+
12
+ if (!dateRegex.test(value)) return "Invalid date format (MM/DD/YYYY)";
13
+
14
+ return true;
15
+ };
16
+
17
+ function requireListRule(value: string[]) {
18
+ return value.length ? "" : "Required";
19
+ }
20
+
21
+ const minOneMonthAdvance = (value: string): boolean | string => {
22
+ if (!/^(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/\d{4}$/.test(value)) {
23
+ return "Invalid date format (MM/DD/YYYY)";
24
+ }
25
+
26
+ const [month, day, year] = value.split("/").map(Number);
27
+ const selectedDate = new Date(year, month - 1, day);
28
+
29
+ const currentDate = new Date();
30
+ const minDate = new Date();
31
+ minDate.setMonth(currentDate.getMonth() + 1); // 1 month in advance
32
+
33
+ return (
34
+ selectedDate >= minDate || "Date must be at least 1 month in advance"
35
+ );
36
+ };
37
+
38
+ const passwordRule = (v: string) =>
39
+ v.length >= 8 || "Password must be at least 8 characters long";
40
+
41
+ const confirmPasswordRule =
42
+ (newPassword: Ref<string>, confirmPassword: Ref<string>) => () =>
43
+ confirmPassword.value === newPassword.value || "Passwords must match";
44
+
45
+ const validateEmail = (email: Ref<string>, emailErrors: Ref<string[]>) => {
46
+ emailErrors.value = [];
47
+
48
+ if (!email.value) {
49
+ emailErrors.value.push("Please input your email");
50
+ } else {
51
+ const result = emailRule(email.value);
52
+ if (result !== true) {
53
+ emailErrors.value.push(result);
54
+ }
55
+ }
56
+ };
57
+
58
+ const validateWord = (value: string): boolean | string => {
59
+ if (value.trim().length < 3) return "Must be at least 3 characters";
60
+ return true;
61
+ };
62
+
63
+ const validatePassword = (
64
+ password: Ref<string>,
65
+ passwordErrors: Ref<string[]>
66
+ ) => {
67
+ passwordErrors.value = [];
68
+
69
+ if (!password.value) {
70
+ passwordErrors.value.push("Please input your password");
71
+ } else if (!passwordRule(password.value)) {
72
+ passwordErrors.value.push("Password must be at least 8 characters long");
73
+ }
74
+ };
75
+
76
+ function back() {
77
+ useRouter().back();
78
+ }
79
+
80
+ function debounce<T extends (...args: any[]) => void>(
81
+ func: T,
82
+ wait: number
83
+ ): (...args: Parameters<T>) => void {
84
+ let timeoutId: ReturnType<typeof setTimeout>;
85
+
86
+ return function (...args: Parameters<T>) {
87
+ clearTimeout(timeoutId);
88
+ timeoutId = setTimeout(() => func(...args), wait);
89
+ };
90
+ }
91
+
92
+ function insertBetween(arr: any, elementToInsert: any) {
93
+ return arr.flatMap((item: any, index: number, array: any) =>
94
+ index < array.length - 1 ? [item, elementToInsert] : [item]
95
+ );
96
+ }
97
+
98
+ function getNameInitials(name: string) {
99
+ if (typeof name !== "string" || name.trim() === "") {
100
+ return ""; // Default fallback for invalid input
101
+ }
102
+
103
+ const words = name.trim().split(/\s+/); // Split by spaces
104
+
105
+ if (words.length === 1) {
106
+ return words[0].slice(0, 2).toUpperCase(); // Take first two letters of the single word
107
+ }
108
+
109
+ return words
110
+ .map((word) => word[0]?.toUpperCase()) // Take the first letter of each word and capitalize
111
+ .join("")
112
+ .slice(0, 2); // Limit to 2 characters
113
+ }
114
+
115
+ function getDimensions(
116
+ file: File
117
+ ): Promise<{ width: number; height: number }> {
118
+ const img = new Image();
119
+ img.src = URL.createObjectURL(file);
120
+ return new Promise((resolve, reject) => {
121
+ img.onload = () => {
122
+ const width = img.width;
123
+ const height = img.height;
124
+ resolve({ width, height });
125
+ };
126
+
127
+ img.onerror = () => {
128
+ reject(new Error("Failed to read image dimensions"));
129
+ };
130
+ });
131
+ }
132
+
133
+ async function getCountries() {
134
+ try {
135
+ const countries = await useNuxtApp().$api<Array<Record<string, any>>>(
136
+ "https://restcountries.com/v3.1/all?fields=name,idd",
137
+ { method: "GET" }
138
+ );
139
+
140
+ const uniqueCountries = new Map();
141
+
142
+ countries.forEach((country) => {
143
+ let suffixes = country.idd?.suffixes?.[0] || "";
144
+ let code = `${country.idd?.root || ""}${suffixes}`;
145
+ let name = country.name?.common || "";
146
+
147
+ if (name && code && !uniqueCountries.has(code)) {
148
+ uniqueCountries.set(code, { index: `${name}-${code}`, name, code });
149
+ }
150
+ });
151
+
152
+ return Array.from(uniqueCountries.values()).sort((a, b) =>
153
+ a.name.localeCompare(b.name)
154
+ );
155
+ } catch (error) {
156
+ console.error(error);
157
+ throw new Error("Failed to fetch countries.");
158
+ }
159
+ }
160
+
161
+ const formatter = new Intl.NumberFormat("en-US", {
162
+ minimumFractionDigits: 2,
163
+ maximumFractionDigits: 2,
164
+ });
165
+
166
+ function formatNumber(
167
+ amount: number,
168
+ options: {
169
+ currency?: string;
170
+ locale?: string;
171
+ useSymbol?: boolean;
172
+ decimalPlaces?: number;
173
+ } = {}
174
+ ): string {
175
+ const {
176
+ currency,
177
+ locale = "en-US",
178
+ useSymbol = false,
179
+ decimalPlaces = 2,
180
+ } = options;
181
+
182
+ return new Intl.NumberFormat(locale, {
183
+ style: useSymbol && currency ? "currency" : "decimal",
184
+ currency: currency || "USD", // Default currency (ignored if `useSymbol` is false)
185
+ minimumFractionDigits: decimalPlaces,
186
+ maximumFractionDigits: decimalPlaces,
187
+ }).format(amount);
188
+ }
189
+
190
+ // 🔹 Examples:
191
+ // console.log(formatNumber(1234.56)); // "1,234.56" (comma separator, 2 decimals)
192
+ // console.log(formatNumber(1234.56, { decimalPlaces: 0 })); // "1,234" (no decimals)
193
+ // console.log(formatNumber(1234.56, { useSymbol: true, currency: "USD" })); // "$1,234.56"
194
+ // console.log(formatNumber(1234.56, { useSymbol: true, currency: "SGD", decimalPlaces: 0 })); // "S$1,234"
195
+ // console.log(formatNumber(1234.56, { useSymbol: false, decimalPlaces: 0 })); // "1,234"
196
+ // console.log(formatNumber(1234.56, { useSymbol: true, currency: "EUR", locale: "de-DE" })); // "1.234,56 €"
197
+ // console.log(formatNumber(1234.56, { useSymbol: true, currency: "EUR", locale: "de-DE", decimalPlaces: 0 })); // "1.234 €"
198
+
199
+ function convertPermissionsToArray(permissions: TPermissions) {
200
+ return Object.entries(permissions).flatMap(([resource, actions]) =>
201
+ Object.keys(actions).map((action) => `${resource}:${action}`)
202
+ );
203
+ }
204
+
205
+ function redirect(link: string, page?: string) {
206
+ const href = page ? `${link}/${page}` : link;
207
+
208
+ window.location.href = href;
209
+ }
210
+
211
+ function computeTieredCost(seats: number, tiers: TPromoTier[]) {
212
+ let totalCost = 0;
213
+
214
+ for (let i = 0; i < tiers.length; i++) {
215
+ let { min, max, price } = tiers[i];
216
+
217
+ if (max === 0) max = Infinity; // Handle unlimited tier
218
+
219
+ if (seats >= min) {
220
+ let seatsInTier = Math.min(seats, max) - min + 1;
221
+ totalCost += seatsInTier * price;
222
+ }
223
+
224
+ if (seats <= max) break; // Stop when all seats are counted
225
+ }
226
+
227
+ return totalCost;
228
+ }
229
+
230
+ const getColorStatus = (status: string) => {
231
+ switch (status.toLowerCase()) {
232
+ case "in-progress":
233
+ case "in progress":
234
+ return "warning";
235
+ case "to-do":
236
+ case "to do":
237
+ return "#052439";
238
+ case "complete":
239
+ case "active":
240
+ return "success";
241
+
242
+ case "suspended":
243
+ return "error";
244
+
245
+ default:
246
+ return "grey";
247
+ }
248
+ };
249
+
250
+ function getOrigin() {
251
+ if (process.client) {
252
+ return window.location.origin;
253
+ }
254
+ return "";
255
+ }
256
+
257
+ const search = useState("search", () => "");
258
+
259
+ //returns Oct 16, 2025, 03:45 PM // N/A format
260
+ function formatDate(dateString: string) {
261
+ if (!dateString) return "N/A";
262
+
263
+ const date = new Date(dateString);
264
+ return date.toLocaleDateString("en-US", {
265
+ year: "numeric",
266
+ month: "short",
267
+ day: "numeric",
268
+ hour: "2-digit",
269
+ minute: "2-digit",
270
+ });
271
+ }
272
+
273
+ // returns 16/10/2025, 15:45 format
274
+ function UTCToLocalTIme(UTCDateTime: string) {
275
+ if (!UTCDateTime) return "";
276
+ const local = new Date(UTCDateTime);
277
+
278
+ const formatted = local.toLocaleString("en-GB", {
279
+ day: "2-digit",
280
+ month: "2-digit",
281
+ year: "numeric",
282
+ hour: "2-digit",
283
+ minute: "2-digit",
284
+ hour12: false, // 24-hour format
285
+ });
286
+
287
+ return formatted;
288
+ }
289
+
290
+ // dd/mm/yyyy in local time
291
+ function formatDateDDMMYYYYLocal(dateString: string) {
292
+ if (!dateString) return "-";
293
+
294
+ const date = new Date(dateString);
295
+ const day = String(date.getDate()).padStart(2, "0");
296
+ const month = String(date.getMonth() + 1).padStart(2, "0");
297
+ const year = date.getFullYear();
298
+
299
+ return `${day}/${month}/${year}`;
300
+ }
301
+
302
+ function formatNature(value: string): string {
303
+ if (!value) return "";
304
+ return value
305
+ .split("_")
306
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
307
+ .join(" ");
308
+ }
309
+
310
+ function replaceMatch(str: string, match: string, replace: string) {
311
+ return str.replace(new RegExp(match, "g"), replace);
312
+ }
313
+
314
+ function setRouteParams(...args: Array<Record<string, string>>) {
315
+ const params: Record<string, string> = {};
316
+
317
+ args.forEach((arg) => {
318
+ Object.entries(arg).forEach(([key, value]) => {
319
+ if (value !== undefined && value !== null && value !== "") {
320
+ params[key] = value;
321
+ }
322
+ });
323
+ });
324
+
325
+ return params;
326
+ }
327
+
328
+ function toOrdinal(n: number): string {
329
+ const s = ["th", "st", "nd", "rd"];
330
+ const v = n % 100;
331
+ return n + (s[(v - 20) % 10] || s[v] || s[0]);
332
+ }
333
+
334
+ function formatDateISO8601(date: Date): string {
335
+ const pad = (n: number) => n.toString().padStart(2, '0');
336
+ const year = date.getFullYear();
337
+ const month = pad(date.getMonth() + 1);
338
+ const day = pad(date.getDate());
339
+ const hours = pad(date.getHours());
340
+ const minutes = pad(date.getMinutes());
341
+ return `${year}-${month}-${day}T${hours}:${minutes}`;
342
+ }
343
+
344
+ function isValidBaseURL(baseURL: string | null) {
345
+ if (!baseURL) return true;
346
+
347
+ // Check if the baseURL starts with http:// or https://
348
+ if (!baseURL.startsWith("http://") && !baseURL.startsWith("https://")) {
349
+ return 'Base URL should start with "https://" or "http://"';
350
+ }
351
+
352
+ try {
353
+ const url = new URL(baseURL);
354
+
355
+ // Reject if there's any path beyond root
356
+ if (url.pathname !== "/" && url.pathname !== "") {
357
+ return "Invalid Base URL";
358
+ }
359
+
360
+ // Also reject if there’s a query or hash
361
+ if (url.search || url.hash) {
362
+ return "Invalid Base URL";
363
+ }
364
+
365
+ return ["http:", "https:"].includes(url.protocol)
366
+ ? true
367
+ : "Invalid Base URL";
368
+ } catch (e: any) {
369
+ return "Invalid Base URL";
370
+ }
371
+ }
372
+
373
+ function formatCamelCaseToWords(key: string){
374
+ if(!key) return "";
375
+ return key
376
+ .replace(/([A-Z])/g, ' $1')
377
+ .replace(/^./, str => str.toUpperCase());
378
+ }
379
+
380
+
381
+ function calculateRemainingTime(
382
+ item: Date | string | number | null | undefined
383
+ ) {
384
+ if (!item) return -1;
385
+ const _date = new Date(item);
386
+ if (isNaN(_date.getTime())) return -1;
387
+ const creationTime = _date.getTime();
388
+ const currentTime = Date.now();
389
+ const differenceInMillis = currentTime - creationTime;
390
+ const differenceInSeconds = Math.floor(differenceInMillis / 1000);
391
+ const desiredDurationInSeconds = 24 * 60 * 60;
392
+ const remainingTimeInSeconds = desiredDurationInSeconds - differenceInSeconds;
393
+ console.log(remainingTimeInSeconds);
394
+ return remainingTimeInSeconds;
395
+ }
396
+
397
+ const formatTime = (seconds: number) => {
398
+ if (seconds <= 0 || isNaN(seconds)) return "00h 00m";
399
+ const hours = Math.floor(seconds / 3600);
400
+ const minutes = Math.floor((seconds % 3600) / 60);
401
+ return `${String(hours).padStart(2, "0")}h ${String(minutes).padStart(
402
+ 2,
403
+ "0"
404
+ )}m`;
405
+ };
406
+
407
+ return {
408
+ requiredRule,
409
+ emailRule,
410
+ passwordRule,
411
+ confirmPasswordRule,
412
+ validateEmail,
413
+ validatePassword,
414
+ back,
415
+ debounce,
416
+ insertBetween,
417
+ getNameInitials,
418
+ getDimensions,
419
+ validateWord,
420
+ getCountries,
421
+ formatter,
422
+ formatNumber,
423
+ validateDate,
424
+ minOneMonthAdvance,
425
+ requireListRule,
426
+ convertPermissionsToArray,
427
+ redirect,
428
+ computeTieredCost,
429
+ getColorStatus,
430
+ getOrigin,
431
+ search,
432
+ formatDate,
433
+ UTCToLocalTIme,
434
+ formatNature,
435
+ replaceMatch,
436
+ setRouteParams,
437
+ toOrdinal,
438
+ formatDateISO8601,
439
+ isValidBaseURL,
440
+ formatCamelCaseToWords,
441
+ calculateRemainingTime,
442
+ formatTime,
443
+ formatDateDDMMYYYYLocal
444
+ };
445
+ }
@@ -0,0 +1,34 @@
1
+ export default function useVerification() {
2
+ function getVerifications({
3
+ status = "pending",
4
+ type = "user-invite",
5
+ search = "",
6
+ page = 1,
7
+ email = "",
8
+ app = ""
9
+ } = {}): Promise<{
10
+ items: TMiniVerification[];
11
+ pages: number;
12
+ pageRange: string;
13
+ }> {
14
+ return useNuxtApp().$api("/api/verifications", {
15
+ method: "GET",
16
+ query: { status, search, page, type, email, app },
17
+ });
18
+ }
19
+
20
+ function cancelUserInvitation(id: string) {
21
+ console.log("Calling API to cancel:", id);
22
+ return useNuxtApp().$api<Record<string, any>>(
23
+ `/api/verifications/${id}/cancel`,
24
+ {
25
+ method: "PUT",
26
+ }
27
+ );
28
+ }
29
+
30
+ return {
31
+ getVerifications,
32
+ cancelUserInvitation,
33
+ };
34
+ }
@@ -0,0 +1,120 @@
1
+ export default function () {
2
+ type TVisitorSelection = {
3
+ label: string;
4
+ value: TVisitorType;
5
+ };
6
+
7
+ const visitorSelection: TVisitorSelection[] =[
8
+ // { label: "Guest", value: "guest" },
9
+ { label: "Contractor", value: "contractor" },
10
+ { label: "Walk-In", value: "walk-in" },
11
+ { label: "Delivery", value: "delivery" },
12
+ { label: "Pick-Up", value: "pick-up" },
13
+ { label: "Drop-Off", value: "drop-off" },
14
+ ];
15
+
16
+ const typeFieldMap: Record<TVisitorType, (keyof TVisitorPayload)[]> = {
17
+ guest: ['name', 'nric', 'contact', 'company', 'block', 'level', 'unit' , 'unitName', 'remarks'],
18
+ contractor: ['contractorType', 'name', 'nric', 'company', 'contact', 'plateNumber', 'block', 'level', 'unit', 'unitName', 'remarks'],
19
+ 'walk-in': ['name', 'company', 'nric', 'contact', 'block', 'level', 'unit' , 'unitName', 'remarks'],
20
+ delivery: ['attachments', 'name', 'deliveryType', 'company', 'nric', 'contact', 'plateNumber', 'block', 'level', 'unit' , 'unitName', 'remarks'],
21
+ 'pick-up': ['plateNumber', 'block', 'remarks'],
22
+ 'drop-off': ['plateNumber', 'block', 'remarks'],
23
+ }
24
+
25
+ const contractorTypes = [
26
+ { title: "Estate Contractor", value: "estate-contractor" },
27
+ { title: "Home Contractor", value: "home-contractor" },
28
+ { title: "Property Agent", value: "property-agent" },
29
+ { title: "House Mover", value: "house-mover" },
30
+ ];
31
+
32
+
33
+ type GetVisitorsParams = {
34
+ page?: number
35
+ limit?: number
36
+ sort?: "asc" | "desc"
37
+ search?: string
38
+ org?: string
39
+ site?: string
40
+ dateTo?: string
41
+ dateFrom?: string
42
+ type?: string
43
+ status?: string
44
+ checkedOut?: boolean
45
+ }
46
+
47
+ async function getVisitors({
48
+ page = 1,
49
+ limit = 10,
50
+ sort = "asc",
51
+ search = "",
52
+ org = "",
53
+ site = "",
54
+ dateTo = "",
55
+ dateFrom = "",
56
+ type = "",
57
+ status = "registered",
58
+ // status = "registered"
59
+ checkedOut
60
+ }: GetVisitorsParams = {}) {
61
+ return await useNuxtApp().$api<Record<string, any>>(
62
+ "/api/visitor-transactions",
63
+ {
64
+ method: "GET",
65
+ query: {
66
+ page,
67
+ limit,
68
+ sort,
69
+ search,
70
+ org,
71
+ site,
72
+ dateTo,
73
+ dateFrom,
74
+ type,
75
+ status,
76
+ checkedOut
77
+ },
78
+ }
79
+ );
80
+ }
81
+
82
+ async function createVisitor(payload: Partial<TVisitorPayload>) {
83
+ return await useNuxtApp().$api<Record<string, any>>(
84
+ "/api/visitor-transactions",
85
+ {
86
+ method: "POST",
87
+ body: payload,
88
+ }
89
+ );
90
+ }
91
+
92
+ async function updateVisitor(_id: string, payload: Partial<TVisitorPayload>) {
93
+ return await useNuxtApp().$api<Record<string, any>>(
94
+ `/api/visitor-transactions/id/${_id}`,
95
+ {
96
+ method: "PUT",
97
+ body: payload,
98
+ }
99
+ );
100
+ }
101
+
102
+ async function deleteVisitor(_id: string) {
103
+ return await useNuxtApp().$api<Record<string, any>>(
104
+ `/api/visitor-transactions/id/${_id}`,
105
+ {
106
+ method: "DELETE",
107
+ }
108
+ );
109
+ }
110
+
111
+ return {
112
+ typeFieldMap,
113
+ visitorSelection,
114
+ contractorTypes,
115
+ createVisitor,
116
+ getVisitors,
117
+ updateVisitor,
118
+ deleteVisitor,
119
+ };
120
+ }
@@ -0,0 +1,85 @@
1
+ export default function useWorkOrder() {
2
+ const workOrders = useState<Array<TWorkOrder>>("workOrders", () => []);
3
+ const page = useState("page", () => 1);
4
+ const pages = useState("pages", () => 0);
5
+ const pageRange = useState("pageRange", () => "-- - -- of --");
6
+ const workOrder = useState<TWorkOrder>("workOrder", () => ({
7
+ _id: "",
8
+ category: "",
9
+ categoryInfo: "",
10
+ subject: "",
11
+ description: "",
12
+ createdBy: "",
13
+ service: "",
14
+ provider: "",
15
+ organization: "",
16
+ site: "",
17
+ createdByName: "",
18
+ assignee: "",
19
+ location: "",
20
+ attachments: [],
21
+ feedback: "",
22
+ status: "",
23
+ createdAt: "",
24
+ updatedAt: "",
25
+ deletedAt: "",
26
+ highPriority: false,
27
+ block: "",
28
+ level: "",
29
+ unit: "",
30
+ serviceProvider: "",
31
+ }));
32
+
33
+ async function getWorkOrders({
34
+ page = 1,
35
+ site = "",
36
+ status = "to-do",
37
+ search = "",
38
+ limit = 10,
39
+ category = "",
40
+ } = {}) {
41
+ try {
42
+ return useNuxtApp().$api<Record<string, any>>(
43
+ `/api/work-orders/site/${site}/status/${status}`,
44
+ {
45
+ method: "GET",
46
+ query: { page, search, limit, category },
47
+ }
48
+ );
49
+ } catch (err) {
50
+ console.error(err);
51
+ }
52
+ }
53
+
54
+ function getWorkOrderById(id: string) {
55
+ return useNuxtApp().$api<TWorkOrder>(`/api/work-orders/${id}`, {
56
+ method: "GET",
57
+ });
58
+ }
59
+
60
+ function createWorkOrder(payload: TWorkOrderCreate) {
61
+ return useNuxtApp().$api<Record<string, any>>("/api/work-orders", {
62
+ method: "POST",
63
+ body: payload,
64
+ });
65
+ }
66
+
67
+ function updateWorkOrder(id: string, payload: object) {
68
+ return useNuxtApp().$api<Record<string, any>>(`/api/work-orders/${id}`, {
69
+ method: "PUT",
70
+ body: payload,
71
+ });
72
+ }
73
+
74
+ return {
75
+ workOrders,
76
+ workOrder,
77
+ page,
78
+ pages,
79
+ pageRange,
80
+ createWorkOrder,
81
+ getWorkOrders,
82
+ getWorkOrderById,
83
+ updateWorkOrder,
84
+ };
85
+ }