@goweekdays/layer-common 1.3.0 → 1.3.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @goweekdays/layer-common
2
2
 
3
+ ## 1.3.2
4
+
5
+ ### Patch Changes
6
+
7
+ - d3b466d: Revamp member and verification
8
+
9
+ ## 1.3.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 8e6b91a: Fix sign up and member invite
14
+
3
15
  ## 1.3.0
4
16
 
5
17
  ### Minor Changes
@@ -1,20 +1,5 @@
1
1
  <template>
2
2
  <v-row no-gutters>
3
- <v-col cols="12" class="mb-2">
4
- <v-row no-gutters>
5
- <v-btn
6
- class="text-none mr-2"
7
- rounded="pill"
8
- variant="tonal"
9
- :to="props.inviteRoute"
10
- size="large"
11
- v-if="props.inviteMember"
12
- >
13
- Invite member
14
- </v-btn>
15
- </v-row>
16
- </v-col>
17
-
18
3
  <v-col cols="12">
19
4
  <v-card
20
5
  width="100%"
@@ -46,24 +31,11 @@
46
31
  <template #extension>
47
32
  <v-tabs>
48
33
  <v-tab
49
- :to="{
50
- name: props.route,
51
- params: props.org
52
- ? { status: 'pending', org: props.org }
53
- : { status: 'active' },
54
- }"
55
- >
56
- Pending
57
- </v-tab>
58
- <v-tab
59
- :to="{
60
- name: props.route,
61
- params: props.org
62
- ? { status: 'expired', org: props.org }
63
- : { status: 'pending' },
64
- }"
34
+ v-for="(routeStatus, routeStatusIndex) in props.routeStatus"
35
+ :key="routeStatusIndex"
36
+ :to="routeStatus"
65
37
  >
66
- Expired
38
+ {{ routeStatus.title }}
67
39
  </v-tab>
68
40
  </v-tabs>
69
41
  </template>
@@ -90,6 +62,29 @@
90
62
  </v-data-table>
91
63
  </v-card>
92
64
  </v-col>
65
+
66
+ <v-dialog v-model="dialogView" width="450" persistent>
67
+ <MemberInvite
68
+ title="Invite Details"
69
+ :app="props.app"
70
+ v-model="invitation"
71
+ @close="setInvite({ dialog: false })"
72
+ @cancel:invite="handleOpenCancelInvite()"
73
+ mode="view"
74
+ />
75
+ </v-dialog>
76
+
77
+ <v-dialog v-model="dialogCancelInvite" width="450" persistent>
78
+ <ConfirmationPrompt
79
+ title="Cancel Invite"
80
+ action="Cancel Invite"
81
+ :content="`Are you sure you want to cancel invitation to ${invitation.email}?`"
82
+ @cancel="dialogCancelInvite = false"
83
+ @confirm="submitCancelInvite()"
84
+ v-model:message="message"
85
+ :disabled="disabledDelete"
86
+ />
87
+ </v-dialog>
93
88
  </v-row>
94
89
  </template>
95
90
 
@@ -105,27 +100,20 @@ const props = defineProps({
105
100
  },
106
101
  app: {
107
102
  type: String,
108
- default: "organization",
109
- },
110
- inviteMember: {
111
- type: Boolean,
112
- default: false,
113
- },
114
- inviteRoute: {
115
- type: Object as PropType<Record<string, any>>,
116
- default: () => ({
117
- name: "index",
118
- params: {},
119
- }),
103
+ default: "org",
120
104
  },
121
105
  route: {
122
106
  type: String,
123
107
  default: "index",
124
108
  },
109
+ routeStatus: {
110
+ type: Array as PropType<
111
+ { title: string; name: string; params?: Record<string, any> }[]
112
+ >,
113
+ default: () => [],
114
+ },
125
115
  });
126
116
 
127
- const organization = (useRoute().params.organization as string) ?? "";
128
-
129
117
  const headers = [
130
118
  {
131
119
  title: "Date",
@@ -137,11 +125,14 @@ const headers = [
137
125
  },
138
126
  {
139
127
  title: "App",
140
- value: "metadata.app",
128
+ value: "metadata.roleName",
141
129
  },
142
130
  ];
143
131
 
144
- const { getVerifications } = useVerification();
132
+ const { getVerifications, cancelInvite } = useVerification();
133
+ const { invitation } = useMember();
134
+
135
+ const dialogView = ref(false);
145
136
 
146
137
  const page = ref(1);
147
138
  const pages = ref(0);
@@ -180,11 +171,27 @@ watchEffect(() => {
180
171
  }
181
172
  });
182
173
 
183
- function tableRowClickHandler(_: any, row: any) {
184
- const item = items.value[row.index];
185
- useRouter().push({
186
- name: "roles-permissions-id",
187
- params: { id: item._id },
174
+ function setInvite({
175
+ dialog = true,
176
+ data = useMember().invitation.value,
177
+ } = {}) {
178
+ Object.assign(invitation.value, JSON.parse(JSON.stringify(data)));
179
+
180
+ if (dialog) {
181
+ dialogView.value = dialog;
182
+ }
183
+ }
184
+
185
+ function tableRowClickHandler(_: any, data: any) {
186
+ setInvite({
187
+ data: {
188
+ _id: data.item._id,
189
+ email: data.item.email,
190
+ role: data.item.metadata.role,
191
+ org: data.item.metadata.org,
192
+ app: data.item.metadata.app,
193
+ },
194
+ dialog: true,
188
195
  });
189
196
  }
190
197
 
@@ -198,6 +205,29 @@ watch(selectAll, (curr) => {
198
205
  selected.value.push(...ids);
199
206
  }
200
207
  });
208
+
209
+ const dialogCancelInvite = ref(false);
210
+ const disabledDelete = ref(false);
211
+ const message = ref("");
212
+
213
+ function handleOpenCancelInvite() {
214
+ dialogCancelInvite.value = true;
215
+ dialogView.value = false;
216
+ }
217
+
218
+ async function submitCancelInvite() {
219
+ disabledDelete.value = true;
220
+ try {
221
+ await cancelInvite(invitation.value._id ?? "");
222
+ await getInvitations();
223
+
224
+ dialogCancelInvite.value = false;
225
+ } catch (error: any) {
226
+ message.value = error.response._data.message || "An error occurred.";
227
+ } finally {
228
+ disabledDelete.value = false;
229
+ }
230
+ }
201
231
  </script>
202
232
 
203
233
  <style scoped>
@@ -155,7 +155,7 @@
155
155
  rounded="xl"
156
156
  variant="tonal"
157
157
  size="x-large"
158
- @click="logout()"
158
+ :to="{ name: 'logout' }"
159
159
  class="text-none text-subtitle-1 font-weight-regular"
160
160
  >
161
161
  Logout
@@ -37,25 +37,27 @@
37
37
  >
38
38
  <v-divider></v-divider>
39
39
  <v-list-item v-if="attrs.readonly" density="compact">
40
- <template #title class="pl-2">
41
- <span class="text-subtitle-2 text-capitalize">
40
+ <template #title>
41
+ <span class="text-subtitle-2 text-capitalize pl-11">
42
42
  {{ item.title }}
43
43
  </span>
44
44
  </template>
45
45
 
46
- <template #subtitle class="pl-2">
47
- <span class="text-subtitle-2">{{ item.description }}</span>
46
+ <template #subtitle>
47
+ <span class="text-subtitle-2 pl-11">{{
48
+ item.description
49
+ }}</span>
48
50
  </template>
49
51
  </v-list-item>
50
52
 
51
53
  <v-list-item v-else :value="item.key" density="compact">
52
- <template #title class="pl-2">
54
+ <template #title>
53
55
  <span class="text-subtitle-2 text-capitalize">
54
56
  {{ item.title }}
55
57
  </span>
56
58
  </template>
57
59
 
58
- <template #subtitle class="pl-2">
60
+ <template #subtitle>
59
61
  <span class="text-subtitle-2 text-capitalize">
60
62
  {{ item.description }}
61
63
  </span>
@@ -0,0 +1,227 @@
1
+ <template>
2
+ <v-card width="100%">
3
+ <v-toolbar>
4
+ <v-row no-gutters class="fill-height px-6" align="center">
5
+ <span class="font-weight-bold text-h5 text-capitalize">
6
+ {{ localProps.title }}
7
+ </span>
8
+ </v-row>
9
+ </v-toolbar>
10
+
11
+ <v-card-text style="max-height: 100vh; overflow-y: auto">
12
+ <v-form v-model="valid" :disabled="localProps.disabled">
13
+ <v-row no-gutters>
14
+ <v-col cols="12" class="mb-2">
15
+ <v-row no-gutters>
16
+ <InputLabel class="text-capitalize" title="Name" />
17
+ <v-col cols="12">
18
+ <v-text-field v-model="member.name" readonly></v-text-field>
19
+ </v-col>
20
+ </v-row>
21
+ </v-col>
22
+
23
+ <v-col cols="12" class="mb-2">
24
+ <v-row no-gutters>
25
+ <InputLabel class="text-capitalize" title="Role" />
26
+ <v-col cols="12">
27
+ <v-select
28
+ v-model="member.role"
29
+ :items="roles"
30
+ item-title="name"
31
+ item-value="_id"
32
+ :rules="isMutable ? [requiredRule] : []"
33
+ :readonly="!isMutable"
34
+ :loading="loadingRoles"
35
+ ></v-select>
36
+ </v-col>
37
+ </v-row>
38
+ </v-col>
39
+ </v-row>
40
+ </v-form>
41
+
42
+ <v-alert
43
+ v-if="message"
44
+ type="error"
45
+ variant="flat"
46
+ closable
47
+ position="absolute"
48
+ location="bottom"
49
+ style="bottom: 48px"
50
+ @click:close="message = ''"
51
+ width="100%"
52
+ tile
53
+ class="text-caption"
54
+ >
55
+ {{ message }}
56
+ </v-alert>
57
+ </v-card-text>
58
+
59
+ <v-toolbar density="compact">
60
+ <v-row no-gutters>
61
+ <v-col v-if="localProps.mode === 'view'" cols="6">
62
+ <v-btn
63
+ tile
64
+ block
65
+ variant="text"
66
+ class="text-none"
67
+ size="48"
68
+ @click="emits('close')"
69
+ >
70
+ Close
71
+ </v-btn>
72
+ </v-col>
73
+
74
+ <v-col v-if="isMutable" cols="6">
75
+ <v-btn
76
+ tile
77
+ block
78
+ variant="text"
79
+ class="text-none"
80
+ size="48"
81
+ @click="emits('cancel')"
82
+ >
83
+ Cancel
84
+ </v-btn>
85
+ </v-col>
86
+
87
+ <v-col v-if="localProps.mode === 'view'" cols="6">
88
+ <v-menu>
89
+ <template #activator="{ props }">
90
+ <v-btn
91
+ block
92
+ variant="flat"
93
+ color="black"
94
+ class="text-none"
95
+ height="48"
96
+ v-bind="props"
97
+ tile
98
+ >
99
+ More actions
100
+ </v-btn>
101
+ </template>
102
+
103
+ <v-list class="pa-0">
104
+ <v-list-item @click="emits('assign')">
105
+ <v-list-item-title class="text-subtitle-2">
106
+ Assign Role
107
+ </v-list-item-title>
108
+ </v-list-item>
109
+
110
+ <v-list-item
111
+ v-if="member.status === 'active'"
112
+ @click="emits('suspend')"
113
+ class="text-red"
114
+ >
115
+ <v-list-item-title class="text-subtitle-2">
116
+ Suspend Member
117
+ </v-list-item-title>
118
+ </v-list-item>
119
+
120
+ <v-list-item
121
+ v-if="member.status === 'suspended'"
122
+ @click="emits('activate')"
123
+ >
124
+ <v-list-item-title class="text-subtitle-2">
125
+ Unsuspend Member
126
+ </v-list-item-title>
127
+ </v-list-item>
128
+
129
+ <v-list-item @click="emits('delete')">
130
+ <v-list-item-title class="text-subtitle-2">
131
+ Remove Member
132
+ </v-list-item-title>
133
+ </v-list-item>
134
+ </v-list>
135
+ </v-menu>
136
+ </v-col>
137
+
138
+ <v-col v-if="isMutable" cols="6">
139
+ <v-btn
140
+ tile
141
+ block
142
+ variant="flat"
143
+ color="black"
144
+ class="text-none"
145
+ size="48"
146
+ @click="emits('submit')"
147
+ :disabled="!valid || localProps.disabled"
148
+ >
149
+ {{
150
+ localProps.mode === "assign-role" ? "Assign Role" : "Save Changes"
151
+ }}
152
+ </v-btn>
153
+ </v-col>
154
+ </v-row>
155
+ </v-toolbar>
156
+ </v-card>
157
+ </template>
158
+
159
+ <script setup lang="ts">
160
+ const emits = defineEmits([
161
+ "submit",
162
+ "cancel",
163
+ "close",
164
+ "edit",
165
+ "delete",
166
+ "suspend",
167
+ "activate",
168
+ "assign",
169
+ "delete",
170
+ ]);
171
+
172
+ const isMutable = computed(() => ["assign-role"].includes(localProps.mode));
173
+
174
+ const localProps = defineProps({
175
+ title: {
176
+ type: String,
177
+ default: "Member Form",
178
+ },
179
+ app: {
180
+ type: String,
181
+ default: "",
182
+ },
183
+ org: {
184
+ type: String,
185
+ default: "",
186
+ },
187
+ mode: {
188
+ type: String,
189
+ default: "add",
190
+ },
191
+ disabled: {
192
+ type: Boolean,
193
+ default: false,
194
+ },
195
+ });
196
+
197
+ const member = defineModel<TMember>({
198
+ default: () => useMember().member.value,
199
+ });
200
+
201
+ const perm = defineModel<TPerm>("perm", {
202
+ default: () => usePermission().perm.value,
203
+ });
204
+
205
+ const message = defineModel("message", { default: "" });
206
+
207
+ const valid = ref(false);
208
+
209
+ const { requiredRule } = useUtils();
210
+
211
+ const { getAll: getAllRoles } = useRole();
212
+
213
+ const roles = ref<Array<Record<string, any>>>([]);
214
+
215
+ const { data: roleData, status: roleReqStatus } = await useLazyAsyncData(
216
+ `get-all-role-app-${localProps.app}-${localProps.org ?? "org"}`,
217
+ () => getAllRoles({ org: localProps.org, app: localProps.app, limit: 50 })
218
+ );
219
+
220
+ const loadingRoles = computed(() => roleReqStatus.value === "pending");
221
+
222
+ watchEffect(() => {
223
+ if (roleData.value && roleData.value.items) {
224
+ roles.value = roleData.value.items;
225
+ }
226
+ });
227
+ </script>
@@ -0,0 +1,206 @@
1
+ <template>
2
+ <v-card width="100%">
3
+ <v-toolbar>
4
+ <v-row no-gutters class="fill-height px-6" align="center">
5
+ <span class="font-weight-bold text-h5 text-capitalize">
6
+ {{ localProps.title }}
7
+ </span>
8
+ </v-row>
9
+ </v-toolbar>
10
+
11
+ <v-card-text style="max-height: 100vh; overflow-y: auto">
12
+ <v-form v-model="valid" :disabled="localProps.disabled">
13
+ <v-row no-gutters>
14
+ <v-col cols="12" class="mb-1">
15
+ <v-row no-gutters>
16
+ <InputLabel
17
+ class="text-capitalize"
18
+ title="Email"
19
+ :required="isMutable"
20
+ />
21
+ <v-col cols="12">
22
+ <v-text-field
23
+ v-model="invite.email"
24
+ :rules="isMutable ? [requiredRule, emailRule] : []"
25
+ :readonly="!isMutable"
26
+ ></v-text-field>
27
+ </v-col>
28
+ </v-row>
29
+ </v-col>
30
+
31
+ <v-col cols="12" class="mb-1">
32
+ <v-row no-gutters>
33
+ <InputLabel
34
+ class="text-capitalize"
35
+ title="Role"
36
+ :required="isMutable"
37
+ />
38
+ <v-col cols="12">
39
+ <v-select
40
+ v-model="invite.role"
41
+ :items="roles"
42
+ item-title="name"
43
+ item-value="_id"
44
+ :loading="loadingRoles"
45
+ :rules="isMutable ? [requiredRule] : []"
46
+ :readonly="!isMutable"
47
+ ></v-select>
48
+ </v-col>
49
+ </v-row>
50
+ </v-col>
51
+
52
+ <v-col v-if="!localProps.app" cols="12" class="mb-1">
53
+ <v-row no-gutters>
54
+ <InputLabel class="text-capitalize" title="App" required />
55
+ <v-col cols="12">
56
+ <v-select
57
+ v-model="invite.app"
58
+ :rules="[requiredRule]"
59
+ ></v-select>
60
+ </v-col>
61
+ </v-row>
62
+ </v-col>
63
+ </v-row>
64
+ </v-form>
65
+
66
+ <v-alert
67
+ v-if="message"
68
+ type="error"
69
+ variant="flat"
70
+ closable
71
+ position="absolute"
72
+ location="bottom"
73
+ style="bottom: 48px"
74
+ @click:close="message = ''"
75
+ width="100%"
76
+ tile
77
+ class="text-caption"
78
+ >
79
+ {{ message }}
80
+ </v-alert>
81
+ </v-card-text>
82
+
83
+ <v-toolbar density="compact">
84
+ <v-row no-gutters>
85
+ <v-col v-if="localProps.mode === 'add'" cols="6">
86
+ <v-btn
87
+ tile
88
+ block
89
+ variant="text"
90
+ class="text-none"
91
+ size="48"
92
+ @click="emits('cancel')"
93
+ >
94
+ Cancel
95
+ </v-btn>
96
+ </v-col>
97
+
98
+ <v-col v-if="localProps.mode === 'view'" cols="6">
99
+ <v-btn
100
+ tile
101
+ block
102
+ variant="text"
103
+ class="text-none"
104
+ size="48"
105
+ @click="emits('close')"
106
+ >
107
+ Close
108
+ </v-btn>
109
+ </v-col>
110
+
111
+ <v-col v-if="localProps.mode === 'add'" cols="6">
112
+ <v-btn
113
+ tile
114
+ block
115
+ variant="flat"
116
+ color="black"
117
+ class="text-none"
118
+ size="48"
119
+ @click="emits('submit')"
120
+ :disabled="localProps.disabled || !valid"
121
+ >
122
+ Invite
123
+ </v-btn>
124
+ </v-col>
125
+
126
+ <v-col v-if="localProps.mode === 'view'" cols="6">
127
+ <v-btn
128
+ tile
129
+ block
130
+ variant="flat"
131
+ color="black"
132
+ class="text-none"
133
+ size="48"
134
+ @click="emits('cancel:invite')"
135
+ :disabled="localProps.disabled"
136
+ >
137
+ Cancel Invite
138
+ </v-btn>
139
+ </v-col>
140
+ </v-row>
141
+ </v-toolbar>
142
+ </v-card>
143
+ </template>
144
+
145
+ <script setup lang="ts">
146
+ const emits = defineEmits([
147
+ "submit",
148
+ "cancel",
149
+ "edit",
150
+ "delete",
151
+ "close",
152
+ "cancel:invite",
153
+ ]);
154
+
155
+ const localProps = defineProps({
156
+ title: {
157
+ type: String,
158
+ default: "Invite Member",
159
+ },
160
+ app: {
161
+ type: String,
162
+ default: "",
163
+ },
164
+ org: {
165
+ type: String,
166
+ default: "",
167
+ },
168
+ disabled: {
169
+ type: Boolean,
170
+ default: false,
171
+ },
172
+ mode: {
173
+ type: String,
174
+ default: "add",
175
+ },
176
+ });
177
+
178
+ const isMutable = computed(() => ["add"].includes(localProps.mode));
179
+
180
+ const invite = defineModel<TMemberInvitation>({
181
+ default: () => useMember().invitation.value,
182
+ });
183
+
184
+ const message = defineModel("message", { default: "" });
185
+
186
+ const valid = ref(false);
187
+
188
+ const { requiredRule, emailRule } = useUtils();
189
+
190
+ const { getAll: getAllRoles } = useRole();
191
+
192
+ const roles = ref<Array<Record<string, any>>>([]);
193
+
194
+ const { data: roleData, status: roleReqStatus } = await useLazyAsyncData(
195
+ `get-all-role-app-${localProps.app}-${localProps.org ?? "org"}`,
196
+ () => getAllRoles({ org: localProps.org, app: localProps.app, limit: 50 })
197
+ );
198
+
199
+ const loadingRoles = computed(() => roleReqStatus.value === "pending");
200
+
201
+ watchEffect(() => {
202
+ if (roleData.value && roleData.value.items) {
203
+ roles.value = roleData.value.items;
204
+ }
205
+ });
206
+ </script>