@goweekdays/layer-common 1.3.1 → 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,11 @@
1
1
  # @goweekdays/layer-common
2
2
 
3
+ ## 1.3.2
4
+
5
+ ### Patch Changes
6
+
7
+ - d3b466d: Revamp member and verification
8
+
3
9
  ## 1.3.1
4
10
 
5
11
  ### Patch 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>
@@ -13,11 +13,16 @@
13
13
  <v-row no-gutters>
14
14
  <v-col cols="12" class="mb-1">
15
15
  <v-row no-gutters>
16
- <InputLabel class="text-capitalize" title="Email" required />
16
+ <InputLabel
17
+ class="text-capitalize"
18
+ title="Email"
19
+ :required="isMutable"
20
+ />
17
21
  <v-col cols="12">
18
22
  <v-text-field
19
23
  v-model="invite.email"
20
- :rules="[requiredRule, emailRule]"
24
+ :rules="isMutable ? [requiredRule, emailRule] : []"
25
+ :readonly="!isMutable"
21
26
  ></v-text-field>
22
27
  </v-col>
23
28
  </v-row>
@@ -25,7 +30,11 @@
25
30
 
26
31
  <v-col cols="12" class="mb-1">
27
32
  <v-row no-gutters>
28
- <InputLabel class="text-capitalize" title="Role" required />
33
+ <InputLabel
34
+ class="text-capitalize"
35
+ title="Role"
36
+ :required="isMutable"
37
+ />
29
38
  <v-col cols="12">
30
39
  <v-select
31
40
  v-model="invite.role"
@@ -33,7 +42,8 @@
33
42
  item-title="name"
34
43
  item-value="_id"
35
44
  :loading="loadingRoles"
36
- :rules="[requiredRule]"
45
+ :rules="isMutable ? [requiredRule] : []"
46
+ :readonly="!isMutable"
37
47
  ></v-select>
38
48
  </v-col>
39
49
  </v-row>
@@ -72,7 +82,7 @@
72
82
 
73
83
  <v-toolbar density="compact">
74
84
  <v-row no-gutters>
75
- <v-col cols="6">
85
+ <v-col v-if="localProps.mode === 'add'" cols="6">
76
86
  <v-btn
77
87
  tile
78
88
  block
@@ -85,7 +95,20 @@
85
95
  </v-btn>
86
96
  </v-col>
87
97
 
88
- <v-col cols="6">
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">
89
112
  <v-btn
90
113
  tile
91
114
  block
@@ -99,13 +122,35 @@
99
122
  Invite
100
123
  </v-btn>
101
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>
102
140
  </v-row>
103
141
  </v-toolbar>
104
142
  </v-card>
105
143
  </template>
106
144
 
107
145
  <script setup lang="ts">
108
- const emits = defineEmits(["submit", "cancel", "edit", "delete"]);
146
+ const emits = defineEmits([
147
+ "submit",
148
+ "cancel",
149
+ "edit",
150
+ "delete",
151
+ "close",
152
+ "cancel:invite",
153
+ ]);
109
154
 
110
155
  const localProps = defineProps({
111
156
  title: {
@@ -124,8 +169,14 @@ const localProps = defineProps({
124
169
  type: Boolean,
125
170
  default: false,
126
171
  },
172
+ mode: {
173
+ type: String,
174
+ default: "add",
175
+ },
127
176
  });
128
177
 
178
+ const isMutable = computed(() => ["add"].includes(localProps.mode));
179
+
129
180
  const invite = defineModel<TMemberInvitation>({
130
181
  default: () => useMember().invitation.value,
131
182
  });