@iservice365/layer-common 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/.editorconfig +12 -0
- package/.github/workflows/main.yml +17 -0
- package/.github/workflows/publish.yml +39 -0
- package/.nuxtrc +1 -0
- package/.playground/app.vue +36 -0
- package/.playground/eslint.config.mjs +6 -0
- package/.playground/nuxt.config.ts +22 -0
- package/CHANGELOG.md +8 -0
- package/README.md +73 -0
- package/app.vue +3 -0
- package/components/BtnUploadFile.vue +139 -0
- package/components/Input/Date.vue +177 -0
- package/components/Input/Password.vue +22 -0
- package/components/InputLabel.vue +18 -0
- package/components/Layout/Header.vue +248 -0
- package/components/Layout/NavigationDrawer.vue +24 -0
- package/components/ListItem.vue +35 -0
- package/components/LocalPagination.vue +31 -0
- package/components/NavigationItem.vue +59 -0
- package/components/PlaceholderComponent.vue +34 -0
- package/components/Snackbar.vue +23 -0
- package/composables/useLocal.ts +41 -0
- package/composables/useLocalAuth.ts +109 -0
- package/composables/useOrg.ts +127 -0
- package/composables/usePermission.ts +54 -0
- package/composables/useRecapPermission.ts +26 -0
- package/composables/useRole.ts +73 -0
- package/composables/useUser.ts +93 -0
- package/composables/useUtils.ts +109 -0
- package/nuxt.config.ts +60 -0
- package/package.json +33 -0
- package/plugins/API.ts +44 -0
- package/plugins/vuetify.ts +41 -0
- package/tsconfig.json +3 -0
- package/types/local.d.ts +63 -0
- package/types/permission.d.ts +24 -0
- package/types/role.d.ts +11 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-app-bar scroll-behavior="elevate" scroll-threshold="200">
|
|
3
|
+
<div style="width: 264px" class="ml-2">
|
|
4
|
+
<nuxt-link
|
|
5
|
+
class="text-h6 font-weight-medium text-decoration-none APP_NAME"
|
|
6
|
+
:to="{ name: APP_NAME_ROUTE }"
|
|
7
|
+
>
|
|
8
|
+
{{ APP_NAME }}
|
|
9
|
+
</nuxt-link>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<v-row no-gutters>
|
|
13
|
+
<v-col cols="6">
|
|
14
|
+
<v-text-field
|
|
15
|
+
v-model="search"
|
|
16
|
+
width="100%"
|
|
17
|
+
prepend-inner-icon="mdi-magnify"
|
|
18
|
+
variant="solo-filled"
|
|
19
|
+
flat
|
|
20
|
+
density="comfortable"
|
|
21
|
+
hide-details
|
|
22
|
+
rounded="xl"
|
|
23
|
+
/>
|
|
24
|
+
</v-col>
|
|
25
|
+
</v-row>
|
|
26
|
+
|
|
27
|
+
<template #append>
|
|
28
|
+
<v-btn
|
|
29
|
+
icon="mdi-theme-light-dark"
|
|
30
|
+
variant="text"
|
|
31
|
+
class="mx-2"
|
|
32
|
+
@click="toggleTheme"
|
|
33
|
+
/>
|
|
34
|
+
|
|
35
|
+
<v-menu offset="10px" :close-on-content-click="false">
|
|
36
|
+
<template #activator="{ props }">
|
|
37
|
+
<v-btn icon="mdi-dots-grid" variant="text" v-bind="props" />
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<v-card
|
|
41
|
+
width="380"
|
|
42
|
+
max-height="400px"
|
|
43
|
+
elevation="2"
|
|
44
|
+
rounded="xl"
|
|
45
|
+
class="pa-4"
|
|
46
|
+
>
|
|
47
|
+
<v-row no-gutters>
|
|
48
|
+
<v-col cols="12">
|
|
49
|
+
<v-card width="100%" variant="tonal" rounded="t-xl b-0">
|
|
50
|
+
<v-row class="pa-4">
|
|
51
|
+
<v-col cols="4">
|
|
52
|
+
<v-btn
|
|
53
|
+
prepend-icon="mdi-account"
|
|
54
|
+
stacked
|
|
55
|
+
rounded="xl"
|
|
56
|
+
variant="text"
|
|
57
|
+
class="text-capitalize text-subtitle-2"
|
|
58
|
+
@click="redirect(APP_ACCOUNT, 'home')"
|
|
59
|
+
>
|
|
60
|
+
Account
|
|
61
|
+
</v-btn>
|
|
62
|
+
</v-col>
|
|
63
|
+
|
|
64
|
+
<v-col cols="4">
|
|
65
|
+
<v-btn
|
|
66
|
+
prepend-icon="mdi-security"
|
|
67
|
+
stacked
|
|
68
|
+
rounded="xl"
|
|
69
|
+
variant="text"
|
|
70
|
+
class="text-capitalize text-subtitle-2"
|
|
71
|
+
@click="redirect(APP_ADMIN, 'home')"
|
|
72
|
+
>
|
|
73
|
+
Admin
|
|
74
|
+
</v-btn>
|
|
75
|
+
</v-col>
|
|
76
|
+
</v-row>
|
|
77
|
+
</v-card>
|
|
78
|
+
</v-col>
|
|
79
|
+
|
|
80
|
+
<v-col cols=" 12" class="mt-2">
|
|
81
|
+
<v-card
|
|
82
|
+
width="100%"
|
|
83
|
+
min-height="50px"
|
|
84
|
+
variant="tonal"
|
|
85
|
+
rounded="b-xl"
|
|
86
|
+
>
|
|
87
|
+
<v-card-title class="text-center pb-0"> Apps </v-card-title>
|
|
88
|
+
|
|
89
|
+
<v-row no-gutters class="px-2 pb-2">
|
|
90
|
+
<v-col
|
|
91
|
+
v-for="item in apps"
|
|
92
|
+
:key="item.title"
|
|
93
|
+
cols="4"
|
|
94
|
+
class="pa-2"
|
|
95
|
+
>
|
|
96
|
+
<v-btn
|
|
97
|
+
stacked
|
|
98
|
+
width="100%"
|
|
99
|
+
:prepend-icon="item.icon"
|
|
100
|
+
rounded="xl"
|
|
101
|
+
variant="text"
|
|
102
|
+
class="text-center text-capitalize text-caption"
|
|
103
|
+
@click="redirect(item.link, item.landingPage)"
|
|
104
|
+
>
|
|
105
|
+
{{ item.title }}
|
|
106
|
+
</v-btn>
|
|
107
|
+
</v-col>
|
|
108
|
+
</v-row>
|
|
109
|
+
</v-card>
|
|
110
|
+
</v-col>
|
|
111
|
+
</v-row>
|
|
112
|
+
</v-card>
|
|
113
|
+
</v-menu>
|
|
114
|
+
|
|
115
|
+
<v-menu offset="10px" :close-on-content-click="false">
|
|
116
|
+
<template #activator="{ props }">
|
|
117
|
+
<v-btn fab variant="text" icon v-bind="props" class="mx-2">
|
|
118
|
+
<v-avatar color="surface-variant" size="42">
|
|
119
|
+
<v-img
|
|
120
|
+
v-if="currentUser?.profile"
|
|
121
|
+
:src="profile"
|
|
122
|
+
width="42"
|
|
123
|
+
height="42"
|
|
124
|
+
/>
|
|
125
|
+
|
|
126
|
+
<span v-else class="text-h5">{{ getNameInitials(name) }}</span>
|
|
127
|
+
</v-avatar>
|
|
128
|
+
</v-btn>
|
|
129
|
+
</template>
|
|
130
|
+
|
|
131
|
+
<v-card
|
|
132
|
+
width="350"
|
|
133
|
+
max-height="600px"
|
|
134
|
+
elevation="2"
|
|
135
|
+
rounded="xl"
|
|
136
|
+
class="pa-4"
|
|
137
|
+
>
|
|
138
|
+
<v-row no-gutters>
|
|
139
|
+
<v-col cols="12">
|
|
140
|
+
<v-row no-gutters justify="center">
|
|
141
|
+
<v-avatar color="surface-variant" size="75">
|
|
142
|
+
<v-img
|
|
143
|
+
v-if="currentUser?.profile"
|
|
144
|
+
:src="profile"
|
|
145
|
+
width="75"
|
|
146
|
+
height="75"
|
|
147
|
+
/>
|
|
148
|
+
|
|
149
|
+
<span v-else class="text-h5">{{
|
|
150
|
+
getNameInitials(name)
|
|
151
|
+
}}</span>
|
|
152
|
+
</v-avatar>
|
|
153
|
+
</v-row>
|
|
154
|
+
</v-col>
|
|
155
|
+
|
|
156
|
+
<v-col cols="12" class="text-center mt-2 mb-4">
|
|
157
|
+
{{ currentUser?.firstName }} {{ currentUser?.lastName }}
|
|
158
|
+
</v-col>
|
|
159
|
+
|
|
160
|
+
<v-col cols="12">
|
|
161
|
+
<v-btn
|
|
162
|
+
block
|
|
163
|
+
rounded="xl"
|
|
164
|
+
variant="tonal"
|
|
165
|
+
size="x-large"
|
|
166
|
+
class="text-none text-subtitle-1 font-weight-regular"
|
|
167
|
+
@click="logout()"
|
|
168
|
+
>
|
|
169
|
+
Logout
|
|
170
|
+
</v-btn>
|
|
171
|
+
</v-col>
|
|
172
|
+
</v-row>
|
|
173
|
+
</v-card>
|
|
174
|
+
</v-menu>
|
|
175
|
+
</template>
|
|
176
|
+
</v-app-bar>
|
|
177
|
+
</template>
|
|
178
|
+
|
|
179
|
+
<script setup lang="ts">
|
|
180
|
+
import { useTheme } from "vuetify";
|
|
181
|
+
|
|
182
|
+
const search = defineModel("search", { type: String });
|
|
183
|
+
|
|
184
|
+
const { redirect, apps } = useLocal();
|
|
185
|
+
|
|
186
|
+
const { APP_ACCOUNT, APP_ADMIN, APP_NAME, APP_NAME_ROUTE } =
|
|
187
|
+
useRuntimeConfig().public;
|
|
188
|
+
|
|
189
|
+
const theme = useTheme();
|
|
190
|
+
|
|
191
|
+
function toggleTheme() {
|
|
192
|
+
theme.global.name.value = theme.global.current.value.dark ? "light" : "dark";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const { currentUser } = useLocalAuth();
|
|
196
|
+
|
|
197
|
+
const profile = computed(() => {
|
|
198
|
+
return `/api/public/${currentUser.value?.profile}`;
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
function logout() {
|
|
202
|
+
if (APP_NAME.toLowerCase() !== "account") {
|
|
203
|
+
redirect(`${APP_ACCOUNT}/logout`);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
useRouter().push({ name: "logout" });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const name = computed(() => {
|
|
211
|
+
let name = "";
|
|
212
|
+
if (currentUser.value?.firstName) {
|
|
213
|
+
name = currentUser.value.firstName;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (currentUser.value?.lastName) {
|
|
217
|
+
name += ` ${currentUser.value.lastName}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return name;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const { getNameInitials } = useUtils();
|
|
224
|
+
</script>
|
|
225
|
+
|
|
226
|
+
<style scoped>
|
|
227
|
+
.APP_NAME {
|
|
228
|
+
display: inline-block;
|
|
229
|
+
word-wrap: break-word;
|
|
230
|
+
white-space: normal;
|
|
231
|
+
text-align: center;
|
|
232
|
+
color: unset !important;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.open-list {
|
|
236
|
+
transition: transform 0.2s ease-in-out;
|
|
237
|
+
transform: rotate(180deg);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.close-list {
|
|
241
|
+
transition: transform 0.3s ease-in-out;
|
|
242
|
+
transform: rotate(0deg);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.v-list-group__items .v-list-item {
|
|
246
|
+
padding-inline-start: 32px !important;
|
|
247
|
+
}
|
|
248
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-navigation-drawer permanent floating class="pr-2">
|
|
3
|
+
<v-list>
|
|
4
|
+
<slot name="action"/>
|
|
5
|
+
<template
|
|
6
|
+
v-for="(navigationItem, navigationIndex) in props.navigationItems"
|
|
7
|
+
:key="`${navigationItem.route.name}-${navigationIndex}`"
|
|
8
|
+
>
|
|
9
|
+
<NavigationItem
|
|
10
|
+
:title="navigationItem.title"
|
|
11
|
+
:icon="navigationItem.icon"
|
|
12
|
+
:route="navigationItem.route"
|
|
13
|
+
:children="navigationItem.children"
|
|
14
|
+
/>
|
|
15
|
+
</template>
|
|
16
|
+
</v-list>
|
|
17
|
+
</v-navigation-drawer>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup lang="ts">
|
|
21
|
+
const props = defineProps({
|
|
22
|
+
navigationItems: { type: Array<TNavigationItem>, required: true },
|
|
23
|
+
});
|
|
24
|
+
</script>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-list-item :class="props.divider ? border : defaultBorder">
|
|
3
|
+
<v-row>
|
|
4
|
+
<v-col cols="4" class="text-subtitle-2">
|
|
5
|
+
<slot name="title"> List Title </slot>
|
|
6
|
+
</v-col>
|
|
7
|
+
|
|
8
|
+
<v-col cols="8" class="text-subtitle-2">
|
|
9
|
+
<slot name="value"> value </slot>
|
|
10
|
+
</v-col>
|
|
11
|
+
</v-row>
|
|
12
|
+
|
|
13
|
+
<template #append>
|
|
14
|
+
<slot name="append">
|
|
15
|
+
<v-icon size="30">{{ props.icon }}</v-icon>
|
|
16
|
+
</slot>
|
|
17
|
+
</template>
|
|
18
|
+
</v-list-item>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script setup lang="ts">
|
|
22
|
+
const props = defineProps({
|
|
23
|
+
divider: {
|
|
24
|
+
type: Boolean,
|
|
25
|
+
default: true,
|
|
26
|
+
},
|
|
27
|
+
icon: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: "mdi-chevron-right",
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const defaultBorder = "pa-0 pl-8 pr-6 py-4";
|
|
34
|
+
const border = "border-b-sm" + " " + defaultBorder;
|
|
35
|
+
</script>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="arrow-navigation">
|
|
3
|
+
<v-btn icon="mdi-chevron-left" variant="text" density="comfortable" :disabled="page <= 1" @click="decrement" />
|
|
4
|
+
<v-btn
|
|
5
|
+
icon="mdi-chevron-right" variant="text" density="comfortable" :disabled="page >= props.length"
|
|
6
|
+
@click="increment" />
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
const page = defineModel({ type: Number, default: 0 });
|
|
12
|
+
function increment() {
|
|
13
|
+
page.value++;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function decrement() {
|
|
17
|
+
page.value--;
|
|
18
|
+
}
|
|
19
|
+
const emit = defineEmits(['update:value']);
|
|
20
|
+
watch(page, () => {
|
|
21
|
+
emit('update:value', page.value);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const props = defineProps({
|
|
25
|
+
length: {
|
|
26
|
+
type: Number,
|
|
27
|
+
required: true,
|
|
28
|
+
default: 0
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-list-group v-if="children && children.length">
|
|
3
|
+
<template #activator="{ props }">
|
|
4
|
+
<v-list-item
|
|
5
|
+
v-bind="props"
|
|
6
|
+
:prepend-icon="icon"
|
|
7
|
+
rounded="e-pill"
|
|
8
|
+
class="text-subtitle-2"
|
|
9
|
+
>
|
|
10
|
+
{{ title }}
|
|
11
|
+
</v-list-item>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<NavigationItem
|
|
15
|
+
v-for="(child, childIndex) in children"
|
|
16
|
+
:key="`${child.title}-${childIndex}`"
|
|
17
|
+
:title="child.title"
|
|
18
|
+
:icon="child.icon"
|
|
19
|
+
:route="child.route"
|
|
20
|
+
:children="child.children"
|
|
21
|
+
/>
|
|
22
|
+
</v-list-group>
|
|
23
|
+
|
|
24
|
+
<v-list-item
|
|
25
|
+
v-else
|
|
26
|
+
:prepend-icon="icon"
|
|
27
|
+
:to="props.route"
|
|
28
|
+
rounded="e-pill"
|
|
29
|
+
class="text-subtitle-2"
|
|
30
|
+
>
|
|
31
|
+
{{ title }}
|
|
32
|
+
</v-list-item>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
const props = defineProps({
|
|
37
|
+
title: {
|
|
38
|
+
type: String,
|
|
39
|
+
required: true,
|
|
40
|
+
default: "Title",
|
|
41
|
+
},
|
|
42
|
+
icon: {
|
|
43
|
+
type: String,
|
|
44
|
+
required: false,
|
|
45
|
+
default: "",
|
|
46
|
+
},
|
|
47
|
+
route: {
|
|
48
|
+
type: Object,
|
|
49
|
+
default() {
|
|
50
|
+
return { name: "", params: {} };
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
children: {
|
|
54
|
+
type: Array<TNavigationItem>,
|
|
55
|
+
required: false,
|
|
56
|
+
default: () => [],
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
</script>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="maintenance-container">
|
|
3
|
+
<h1>🚧 Page Under Maintenance</h1>
|
|
4
|
+
<p>
|
|
5
|
+
We're currently working on improving this page to serve you better. Please
|
|
6
|
+
check back soon!
|
|
7
|
+
</p>
|
|
8
|
+
<p>Thank you for your patience.</p>
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<style scoped>
|
|
13
|
+
.maintenance-container {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
align-items: center;
|
|
18
|
+
text-align: center;
|
|
19
|
+
min-height: 50vh;
|
|
20
|
+
margin: 0;
|
|
21
|
+
font-family: Arial, sans-serif;
|
|
22
|
+
color: #333;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
h1 {
|
|
26
|
+
font-size: 2rem;
|
|
27
|
+
margin-bottom: 1rem;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
p {
|
|
31
|
+
font-size: 1.1rem;
|
|
32
|
+
line-height: 1.5;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-snackbar v-model="snackbar" :color="props.color">
|
|
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,41 @@
|
|
|
1
|
+
export default function useLocal() {
|
|
2
|
+
const appConfig = useRuntimeConfig().public;
|
|
3
|
+
|
|
4
|
+
const { cookieConfig } = appConfig;
|
|
5
|
+
|
|
6
|
+
function getUserFromCookie() {
|
|
7
|
+
return useCookie("user", cookieConfig).value;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const drawer = useState("drawer", () => true);
|
|
11
|
+
|
|
12
|
+
const { APP_RECAP } = appConfig;
|
|
13
|
+
|
|
14
|
+
const apps = computed(() => {
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
title: "RECAP",
|
|
18
|
+
icon: "mdi-account-group",
|
|
19
|
+
link: APP_RECAP as string,
|
|
20
|
+
landingPage: "requests/status/draft",
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function redirect(link: string, page?: string) {
|
|
26
|
+
const href = page ? `${link}/${page}` : link;
|
|
27
|
+
|
|
28
|
+
window.location.href = href;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const headerSearch = useState("headerSearch", () => "");
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
cookieConfig,
|
|
35
|
+
getUserFromCookie,
|
|
36
|
+
drawer,
|
|
37
|
+
apps,
|
|
38
|
+
redirect,
|
|
39
|
+
headerSearch,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export default function useLocalAuth() {
|
|
2
|
+
const { cookieConfig } = useRuntimeConfig().public;
|
|
3
|
+
|
|
4
|
+
async function login({ email = "", password = "" }) {
|
|
5
|
+
return useNuxtApp().$api<TKeyValuePair>("/api/auth", {
|
|
6
|
+
method: "POST",
|
|
7
|
+
body: JSON.stringify({ email, password }),
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function setToken({
|
|
12
|
+
refreshToken = "",
|
|
13
|
+
accessToken = "",
|
|
14
|
+
user = "",
|
|
15
|
+
org = "",
|
|
16
|
+
}) {
|
|
17
|
+
useCookie("accessToken", cookieConfig).value = accessToken;
|
|
18
|
+
useCookie("refreshToken", cookieConfig).value = refreshToken;
|
|
19
|
+
useCookie("user", cookieConfig).value = user;
|
|
20
|
+
useCookie("org", cookieConfig).value = org;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function clearCookies() {
|
|
24
|
+
useCookie("accessToken", cookieConfig).value = null;
|
|
25
|
+
useCookie("refreshToken", cookieConfig).value = null;
|
|
26
|
+
useCookie("user", cookieConfig).value = null;
|
|
27
|
+
useCookie("organization", cookieConfig).value = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function logout() {
|
|
31
|
+
const refreshToken = useCookie("refreshToken", cookieConfig).value;
|
|
32
|
+
if (refreshToken) {
|
|
33
|
+
try {
|
|
34
|
+
await useNuxtApp().$api(`/api/auth/${refreshToken}`, {
|
|
35
|
+
method: "DELETE",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
clearCookies();
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error("Logout failed:", error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const currentUser = useState((): TUser | null => null);
|
|
46
|
+
|
|
47
|
+
async function getCurrentUser() {
|
|
48
|
+
const user = useCookie("user", cookieConfig).value;
|
|
49
|
+
if (!user) return null;
|
|
50
|
+
const _user = await useNuxtApp().$api<TUser>(`/api/users/id/${user}`, {
|
|
51
|
+
method: "GET",
|
|
52
|
+
});
|
|
53
|
+
currentUser.value = _user;
|
|
54
|
+
return _user;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function forgotPassword(email: string) {
|
|
58
|
+
if (!email) {
|
|
59
|
+
throw new Error("Email is required for password reset request.");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const response = await useNuxtApp().$api("/api/auth/forget-password", {
|
|
64
|
+
method: "POST",
|
|
65
|
+
body: JSON.stringify({ email }),
|
|
66
|
+
headers: { "Content-Type": "application/json" },
|
|
67
|
+
});
|
|
68
|
+
return response;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error("Error in password reset request:", error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function resetPassword(
|
|
76
|
+
otp: string,
|
|
77
|
+
newPassword: string,
|
|
78
|
+
passwordConfirmation: string
|
|
79
|
+
) {
|
|
80
|
+
try {
|
|
81
|
+
return await useNuxtApp().$api("/api/auth/reset-password", {
|
|
82
|
+
method: "POST",
|
|
83
|
+
body: JSON.stringify({ otp, newPassword, passwordConfirmation }),
|
|
84
|
+
headers: { "Content-Type": "application/json" },
|
|
85
|
+
});
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error("Error resetting password:", error);
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function verify(id: string) {
|
|
93
|
+
return useNuxtApp().$api<TKeyValuePair>(`/api/auth/verify/${id}`, {
|
|
94
|
+
method: "GET",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
login,
|
|
100
|
+
logout,
|
|
101
|
+
clearCookies,
|
|
102
|
+
getCurrentUser,
|
|
103
|
+
setToken,
|
|
104
|
+
forgotPassword,
|
|
105
|
+
resetPassword,
|
|
106
|
+
currentUser,
|
|
107
|
+
verify,
|
|
108
|
+
};
|
|
109
|
+
}
|