@iservice365/layer-common 0.1.0 → 0.2.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 (91) hide show
  1. package/.playground/app.vue +7 -2
  2. package/.playground/pages/feedback.vue +30 -0
  3. package/CHANGELOG.md +6 -0
  4. package/components/Chat/Bubbles.vue +53 -0
  5. package/components/Chat/Information.vue +187 -0
  6. package/components/Chat/ListCard.vue +62 -0
  7. package/components/Chat/Message.vue +149 -0
  8. package/components/Chat/Navigation.vue +150 -0
  9. package/components/ConfirmDialog.vue +66 -0
  10. package/components/Container/Standard.vue +33 -0
  11. package/components/Feedback/Form.vue +136 -0
  12. package/components/FeedbackDetail.vue +465 -0
  13. package/components/FeedbackMain.vue +454 -0
  14. package/components/FormDialog.vue +65 -0
  15. package/components/Input/File.vue +203 -0
  16. package/components/Input/ListGroupSelection.vue +96 -0
  17. package/components/Input/NewDate.vue +123 -0
  18. package/components/Input/Number.vue +124 -0
  19. package/components/InvitationMain.vue +284 -0
  20. package/components/Layout/Header.vue +14 -4
  21. package/components/ListView.vue +87 -0
  22. package/components/MemberMain.vue +459 -0
  23. package/components/RolePermissionFormCreate.vue +161 -0
  24. package/components/RolePermissionFormPreviewUpdate.vue +183 -0
  25. package/components/RolePermissionMain.vue +361 -0
  26. package/components/ServiceProviderFormCreate.vue +154 -0
  27. package/components/ServiceProviderMain.vue +195 -0
  28. package/components/SignaturePad.vue +73 -0
  29. package/components/SpecificAttr.vue +53 -0
  30. package/components/SwitchContext.vue +26 -5
  31. package/components/TableList.vue +150 -0
  32. package/components/TableListSecondary.vue +164 -0
  33. package/components/WorkOrder/Create.vue +197 -0
  34. package/components/WorkOrder/ListView.vue +96 -0
  35. package/components/WorkOrder/Main.vue +308 -0
  36. package/components/Workorder.vue +1 -0
  37. package/composables/useAddress.ts +107 -0
  38. package/composables/useCommonPermission.ts +130 -0
  39. package/composables/useCustomer.ts +113 -0
  40. package/composables/useFeedback.ts +117 -0
  41. package/composables/useFile.ts +40 -0
  42. package/composables/useInvoice.ts +18 -0
  43. package/composables/useLocal.ts +24 -4
  44. package/composables/useLocalAuth.ts +58 -14
  45. package/composables/useLocalSetup.ts +52 -0
  46. package/composables/useMember.ts +104 -0
  47. package/composables/useOrg.ts +76 -92
  48. package/composables/usePaymentMethod.ts +101 -0
  49. package/composables/usePrice.ts +15 -0
  50. package/composables/usePromoCode.ts +36 -0
  51. package/composables/useRole.ts +38 -7
  52. package/composables/useServiceProvider.ts +218 -0
  53. package/composables/useSite.ts +108 -0
  54. package/composables/useSubscription.ts +149 -0
  55. package/composables/useUser.ts +38 -14
  56. package/composables/useUtils.ts +218 -6
  57. package/composables/useVerification.ts +33 -0
  58. package/composables/useWorkOrder.ts +68 -0
  59. package/middleware/01.auth.ts +11 -0
  60. package/middleware/02.org.ts +18 -0
  61. package/middleware/03.customer.ts +13 -0
  62. package/nuxt.config.ts +2 -1
  63. package/package.json +7 -3
  64. package/pages/index.vue +3 -0
  65. package/pages/payment-method-linked.vue +31 -0
  66. package/pages/require-customer.vue +56 -0
  67. package/pages/require-organization-membership.vue +47 -0
  68. package/pages/unauthorized.vue +29 -0
  69. package/plugins/API.ts +1 -3
  70. package/plugins/iconify.client.ts +5 -0
  71. package/plugins/vuetify.ts +2 -0
  72. package/public/bg-camera.jpg +0 -0
  73. package/public/bg-city.jpg +0 -0
  74. package/public/bg-condo.jpg +0 -0
  75. package/public/images/icons/delete-icon.png +0 -0
  76. package/public/sprite.svg +1 -0
  77. package/types/address.d.ts +13 -0
  78. package/types/customer.d.ts +15 -0
  79. package/types/feedback.d.ts +63 -0
  80. package/types/local.d.ts +46 -38
  81. package/types/member.d.ts +21 -0
  82. package/types/org.d.ts +13 -0
  83. package/types/permission.d.ts +1 -0
  84. package/types/price.d.ts +17 -0
  85. package/types/promo-code.d.ts +19 -0
  86. package/types/service-provider.d.ts +15 -0
  87. package/types/site.d.ts +13 -0
  88. package/types/subscription.d.ts +23 -0
  89. package/types/user.d.ts +19 -0
  90. package/types/verification.d.ts +20 -0
  91. package/types/work-order.d.ts +40 -0
@@ -6,6 +6,35 @@ export default function useUtils() {
6
6
  return regex.test(v) || "Please enter a valid email address";
7
7
  };
8
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
+
9
38
  const passwordRule = (v: string) =>
10
39
  v.length >= 8 || "Password must be at least 8 characters long";
11
40
 
@@ -26,6 +55,11 @@ export default function useUtils() {
26
55
  }
27
56
  };
28
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
+
29
63
  const validatePassword = (
30
64
  password: Ref<string>,
31
65
  passwordErrors: Ref<string[]>
@@ -66,13 +100,16 @@ export default function useUtils() {
66
100
  return ""; // Default fallback for invalid input
67
101
  }
68
102
 
69
- const initials = name
70
- .trim()
71
- .split(/\s+/) // Split by spaces
72
- .map((word) => word[0]?.toUpperCase()) // Take the first letter of each word and capitalize
73
- .join(""); // Combine all initials
103
+ const words = name.trim().split(/\s+/); // Split by spaces
74
104
 
75
- return initials.slice(0, 2); // Limit to 2 characters
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
76
113
  }
77
114
 
78
115
  function getDimensions(
@@ -93,12 +130,171 @@ export default function useUtils() {
93
130
  });
94
131
  }
95
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
+
96
205
  function redirect(link: string, page?: string) {
97
206
  const href = page ? `${link}/${page}` : link;
98
207
 
99
208
  window.location.href = href;
100
209
  }
101
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
+ function formatDate(dateString: string) {
260
+ if (!dateString) return "N/A";
261
+
262
+ const date = new Date(dateString);
263
+ return date.toLocaleDateString("en-US", {
264
+ year: "numeric",
265
+ month: "short",
266
+ day: "numeric",
267
+ hour: "2-digit",
268
+ minute: "2-digit",
269
+ });
270
+ }
271
+
272
+ function formatNature(value: string): string {
273
+ if (!value) return "";
274
+ return value
275
+ .split("_")
276
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
277
+ .join(" ");
278
+ }
279
+
280
+ function replaceMatch(str: string, match: string, replace: string) {
281
+ return str.replace(new RegExp(match, "g"), replace);
282
+ }
283
+
284
+ function setRouteParams(...args: Array<Record<string, string>>) {
285
+ const params: Record<string, string> = {};
286
+
287
+ args.forEach((arg) => {
288
+ Object.entries(arg).forEach(([key, value]) => {
289
+ if (value !== undefined && value !== null && value !== "") {
290
+ params[key] = value;
291
+ }
292
+ });
293
+ });
294
+
295
+ return params;
296
+ }
297
+
102
298
  return {
103
299
  requiredRule,
104
300
  emailRule,
@@ -111,6 +307,22 @@ export default function useUtils() {
111
307
  insertBetween,
112
308
  getNameInitials,
113
309
  getDimensions,
310
+ validateWord,
311
+ getCountries,
312
+ formatter,
313
+ formatNumber,
314
+ validateDate,
315
+ minOneMonthAdvance,
316
+ requireListRule,
317
+ convertPermissionsToArray,
114
318
  redirect,
319
+ computeTieredCost,
320
+ getColorStatus,
321
+ getOrigin,
322
+ search,
323
+ formatDate,
324
+ formatNature,
325
+ replaceMatch,
326
+ setRouteParams,
115
327
  };
116
328
  }
@@ -0,0 +1,33 @@
1
+ export default function useVerification() {
2
+ function getVerifications({
3
+ status = "pending",
4
+ type = "user-invite",
5
+ search = "",
6
+ page = 1,
7
+ email = "",
8
+ } = {}): Promise<{
9
+ items: TMiniVerification[];
10
+ pages: number;
11
+ pageRange: string;
12
+ }> {
13
+ return useNuxtApp().$api("/api/verifications", {
14
+ method: "GET",
15
+ query: { status, search, page, type, email },
16
+ });
17
+ }
18
+
19
+ function cancelUserInvitation(id: string) {
20
+ console.log("Calling API to cancel:", id);
21
+ return useNuxtApp().$api<Record<string, any>>(
22
+ `/api/verifications/${id}/cancel`,
23
+ {
24
+ method: "PUT",
25
+ }
26
+ );
27
+ }
28
+
29
+ return {
30
+ getVerifications,
31
+ cancelUserInvitation,
32
+ };
33
+ }
@@ -0,0 +1,68 @@
1
+ export default function useFeedback() {
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
+ subject: "",
9
+ description: "",
10
+ createdBy: "",
11
+ service: "",
12
+ provider: "",
13
+ organization: "",
14
+ site: "",
15
+ createdByName: "",
16
+ assignee: "",
17
+ location: "",
18
+ attachments: [],
19
+ feedback: "",
20
+ status: "",
21
+ createdAt: "",
22
+ updatedAt: "",
23
+ deletedAt: "",
24
+ highPriority: false,
25
+ block: "",
26
+ level: "",
27
+ unit: "",
28
+ serviceProvider: "",
29
+ }));
30
+
31
+ async function getWorkOrders({
32
+ page = 1,
33
+ organization = "",
34
+ site = "",
35
+ status = "to-do",
36
+ search = "",
37
+ limit = 10,
38
+ } = {}) {
39
+ try {
40
+ return useNuxtApp().$api<Record<string, any>>(
41
+ `/api/work-orders/organization/${organization}/site/${site}/status/${status}`,
42
+ {
43
+ method: "GET",
44
+ query: { page, search, limit },
45
+ }
46
+ );
47
+ } catch (err) {
48
+ console.error(err);
49
+ }
50
+ }
51
+
52
+ function createWorkOrder(payload: TWorkOrderCreate) {
53
+ return useNuxtApp().$api<Record<string, any>>("/api/work-orders", {
54
+ method: "POST",
55
+ body: payload,
56
+ });
57
+ }
58
+
59
+ return {
60
+ workOrders,
61
+ workOrder,
62
+ page,
63
+ pages,
64
+ pageRange,
65
+ createWorkOrder,
66
+ getWorkOrders,
67
+ };
68
+ }
@@ -0,0 +1,11 @@
1
+ export default defineNuxtRouteMiddleware(async () => {
2
+ const { cookieConfig } = useRuntimeConfig().public;
3
+
4
+ // Get access token from cookies
5
+ const accessToken = useCookie("accessToken", cookieConfig).value;
6
+
7
+ if (!accessToken) {
8
+ // Redirect to login page if no access token
9
+ return navigateTo({ name: "index" });
10
+ }
11
+ });
@@ -0,0 +1,18 @@
1
+ import { z } from "zod";
2
+
3
+ const hexSchema = z
4
+ .string()
5
+ .regex(/^[0-9a-fA-F]{24}$/, "Invalid organization ID");
6
+
7
+ export default defineNuxtRouteMiddleware(async (to) => {
8
+ if (import.meta.server) return;
9
+
10
+ const { organization, org } = to.params;
11
+
12
+ if (!hexSchema.safeParse(organization ?? org).success) {
13
+ return navigateTo(
14
+ { name: "require-organization-membership" },
15
+ { replace: true }
16
+ );
17
+ }
18
+ });
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+
3
+ const hexSchema = z.string().regex(/^[0-9a-fA-F]{24}$/, "Invalid customer ID");
4
+
5
+ export default defineNuxtRouteMiddleware(async (to, from) => {
6
+ if (import.meta.server) return;
7
+
8
+ const site = to.params.site as string;
9
+
10
+ if (!hexSchema.safeParse(site).success) {
11
+ return navigateTo({ name: "require-customer" }, { replace: true });
12
+ }
13
+ });
package/nuxt.config.ts CHANGED
@@ -3,7 +3,7 @@ import vuetify, { transformAssetUrls } from "vite-plugin-vuetify";
3
3
  export default defineNuxtConfig({
4
4
  devtools: { enabled: true },
5
5
  build: {
6
- transpile: ["vuetify"],
6
+ transpile: ["vuetify", "nuxt-signature-pad"],
7
7
  },
8
8
 
9
9
  runtimeConfig: {
@@ -37,6 +37,7 @@ export default defineNuxtConfig({
37
37
  config.plugins.push(vuetify({ autoImport: true }));
38
38
  });
39
39
  },
40
+ "nuxt-signature-pad",
40
41
  ],
41
42
 
42
43
  vite: {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@iservice365/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "0.1.0",
5
+ "version": "0.2.0",
6
6
  "main": "./nuxt.config.ts",
7
7
  "publishConfig": {
8
8
  "access": "public"
@@ -20,14 +20,18 @@
20
20
  "@nuxt/eslint": "latest",
21
21
  "eslint": "^9.14.0",
22
22
  "nuxt": "^3.13.2",
23
- "typescript": "^5.6.3",
23
+ "nuxt-signature-pad": "1.3.0",
24
+ "typescript": "^5.8.3",
24
25
  "vite-plugin-vuetify": "^2.0.4",
25
26
  "vue": "latest",
26
27
  "vuetify": "^3.7.3"
27
28
  },
28
29
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
29
30
  "dependencies": {
31
+ "@iconify/vue": "^5.0.0",
30
32
  "@mdi/font": "^7.4.47",
31
- "sass": "^1.80.6"
33
+ "sass": "^1.80.6",
34
+ "vue3-signature": "^0.2.4",
35
+ "zod": "^3.24.2"
32
36
  }
33
37
  }
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <h1>index</h1>
3
+ </template>
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <v-row no-gutters class="fill-height" justify="center" align-content="center">
3
+ <v-card class="pa-4" elevation="4" color="success lighten-1">
4
+ <v-card-title class="text-white d-flex align-center">
5
+ <v-icon class="mr-2">mdi-check-circle</v-icon>
6
+ Successfully Linked Payment Method
7
+ </v-card-title>
8
+
9
+ <v-card-actions class="d-flex justify-center">
10
+ <v-btn
11
+ color="white"
12
+ variant="tonal"
13
+ rounded="xl"
14
+ size="large"
15
+ @click="closeWindow"
16
+ >Close</v-btn
17
+ >
18
+ </v-card-actions>
19
+ </v-card>
20
+ </v-row>
21
+ </template>
22
+
23
+ <script setup>
24
+ definePageMeta({
25
+ layout: "plain",
26
+ });
27
+
28
+ const closeWindow = () => {
29
+ window.close();
30
+ };
31
+ </script>
@@ -0,0 +1,56 @@
1
+ <template>
2
+ <v-row no-gutters class="fill-height" align="center" justify="center">
3
+ <v-col cols="12" lg="6" md="6" sm="6" class="text-center text-subtitle-1">
4
+ You must have a customer to view this page. Please join or contact your
5
+ administrator for access.
6
+ <v-row no-gutters justify="center" align="center" class="mt-2">
7
+ <v-btn
8
+ @click="goToAccount"
9
+ rounded="xl"
10
+ variant="tonal"
11
+ class="text-none text-subtitle-2"
12
+ >
13
+ Go to Organization Page
14
+ </v-btn>
15
+ <span class="mx-2">or</span>
16
+ <v-btn
17
+ @click="createCustomer"
18
+ rounded="xl"
19
+ variant="tonal"
20
+ class="text-none text-subtitle-2"
21
+ >
22
+ Create an Customer
23
+ </v-btn>
24
+ </v-row>
25
+ </v-col>
26
+ </v-row>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ definePageMeta({
31
+ layout: "plain",
32
+ });
33
+
34
+ const { APP_NAME, APP_ORG } = useRuntimeConfig().public;
35
+
36
+ function goToAccount() {
37
+ window.location.href = `${APP_ORG}`;
38
+ }
39
+
40
+ const { authenticate } = useLocalAuth();
41
+
42
+ authenticate();
43
+
44
+ const { currentUser } = useLocalAuth();
45
+
46
+ function createCustomer() {
47
+ if (APP_NAME.toLowerCase() === "org") {
48
+ navigateTo({
49
+ name: "org-organizations-customers-add",
50
+ params: { organization: currentUser.value?.defaultOrg },
51
+ });
52
+ } else {
53
+ window.location.href = `${APP_ORG}/org/${currentUser.value?.defaultOrg}/customers/add`;
54
+ }
55
+ }
56
+ </script>
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <v-row no-gutters class="fill-height" align="center" justify="center">
3
+ <v-col cols="12" lg="6" md="6" sm="6" class="text-center text-subtitle-1">
4
+ You must be a member of an organization to view this page. Please join or
5
+ contact your administrator for access.
6
+ <v-row no-gutters justify="center" align="center" class="mt-2">
7
+ <v-btn
8
+ @click="goToAccount"
9
+ rounded="xl"
10
+ variant="tonal"
11
+ class="text-none text-subtitle-2"
12
+ >
13
+ Go to Account Page
14
+ </v-btn>
15
+ <span class="mx-2">or</span>
16
+ <v-btn
17
+ @click="createOrg"
18
+ rounded="xl"
19
+ variant="tonal"
20
+ class="text-none text-subtitle-2"
21
+ >
22
+ Create an Organization
23
+ </v-btn>
24
+ </v-row>
25
+ </v-col>
26
+ </v-row>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ definePageMeta({
31
+ layout: "plain",
32
+ });
33
+
34
+ const { APP_ACCOUNT, APP_NAME, APP_ORG } = useRuntimeConfig().public;
35
+
36
+ function goToAccount() {
37
+ window.location.href = `${APP_ACCOUNT}/home`;
38
+ }
39
+
40
+ function createOrg() {
41
+ if (APP_NAME.toLowerCase() === "org") {
42
+ navigateTo({ name: "organizations-create" });
43
+ } else {
44
+ window.location.href = `${APP_ORG}/organizations/create`;
45
+ }
46
+ }
47
+ </script>
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <v-row no-gutters class="fill-height" align="center" justify="center">
3
+ <v-col cols="12" lg="6" md="6" sm="6" class="text-center text-subtitle-1">
4
+ You must be a member of an organization to view this page. Please join or
5
+ contact your administrator for access.
6
+ <v-row no-gutters justify="center" class="mt-2">
7
+ <v-btn
8
+ @click="goToAccount"
9
+ rounded="xl"
10
+ variant="tonal"
11
+ class="text-none text-subtitle-2"
12
+ >
13
+ Go to Account Page
14
+ </v-btn>
15
+ </v-row>
16
+ </v-col>
17
+ </v-row>
18
+ </template>
19
+
20
+ <script setup lang="ts">
21
+ definePageMeta({
22
+ layout: "plain",
23
+ });
24
+
25
+ function goToAccount() {
26
+ const { APP_ACCOUNT } = useRuntimeConfig().public;
27
+ window.location.href = `${APP_ACCOUNT}/home`;
28
+ }
29
+ </script>
package/plugins/API.ts CHANGED
@@ -8,9 +8,7 @@ export default defineNuxtPlugin(() => {
8
8
  retryDelay: 500,
9
9
  onRequest({ options }) {
10
10
  const accessToken = useCookie("accessToken", cookieConfig).value;
11
- options.headers = accessToken
12
- ? { Authorization: `Bearer ${accessToken}` }
13
- : {};
11
+ options.headers.set("Authorization", `Bearer ${accessToken}`);
14
12
  },
15
13
 
16
14
  async onResponseError({ response }) {
@@ -0,0 +1,5 @@
1
+ import { Icon } from "@iconify/vue";
2
+
3
+ export default defineNuxtPlugin((nuxtApp) => {
4
+ nuxtApp.vueApp.component("Icon", Icon);
5
+ });
@@ -31,6 +31,8 @@ export default defineNuxtPlugin((app) => {
31
31
  dark: false,
32
32
  colors: {
33
33
  primary: "#042134",
34
+ "primary-button": "#1867C0",
35
+ "text-primary": "#052439",
34
36
  },
35
37
  },
36
38
  },
Binary file
Binary file
Binary file