Skip to content

Commit d0e126b

Browse files
committed
feat(ui): implement invitation store, types and API wrapper
1 parent 8c354a7 commit d0e126b

File tree

14 files changed

+180
-104
lines changed

14 files changed

+180
-104
lines changed

ui/src/components/Team/Member/MemberInvite.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,11 @@ import CopyWarning from "@/components/User/CopyWarning.vue";
114114
import RoleSelect from "../RoleSelect.vue";
115115
import { BasicRole } from "@/interfaces/INamespace";
116116
import useAuthStore from "@/store/modules/auth";
117-
import useNamespacesStore from "@/store/modules/namespaces";
117+
import useInvitationsStore from "@/store/modules/invitations";
118118
119119
const emit = defineEmits(["update"]);
120120
const authStore = useAuthStore();
121-
const namespacesStore = useNamespacesStore();
121+
const invitationsStore = useInvitationsStore();
122122
const snackbar = useSnackbar();
123123
const showDialog = ref(false);
124124
const isLoading = ref(false);
@@ -182,7 +182,7 @@ const getInvitePayload = () => ({
182182
const generateLinkInvite = async () => {
183183
isLoading.value = true;
184184
try {
185-
invitationLink.value = await namespacesStore.generateInvitationLink(getInvitePayload());
185+
invitationLink.value = await invitationsStore.generateInvitationLink(getInvitePayload());
186186
snackbar.showSuccess("Invitation link generated successfully.");
187187
formWindow.value = "form-2";
188188
} catch (error) {
@@ -195,7 +195,7 @@ const generateLinkInvite = async () => {
195195
const sendEmailInvite = async () => {
196196
isLoading.value = true;
197197
try {
198-
await namespacesStore.sendEmailInvitation(getInvitePayload());
198+
await invitationsStore.sendInvitationEmail(getInvitePayload());
199199
snackbar.showSuccess("Invitation email sent successfully.");
200200
update();
201201
resetFields();

ui/src/components/Team/Member/MemberList.vue

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,6 @@
4141
<td>
4242
<v-icon> mdi-account </v-icon>
4343
{{ member.email }}
44-
</td>
45-
46-
<td class="text-center text-capitalize">
47-
{{ member.role }}
48-
</td>
49-
50-
<td class="text-center text-capitalize">
51-
{{ member.status }}
5244
<v-tooltip
5345
v-if="member.added_at !== '0001-01-01T00:00:00Z'"
5446
activator="parent"
@@ -58,6 +50,10 @@
5850
</v-tooltip>
5951
</td>
6052

53+
<td class="text-center text-capitalize">
54+
{{ member.role }}
55+
</td>
56+
6157
<td class="text-center">
6258
<v-menu
6359
v-if="!isNamespaceOwner(member.role)"
@@ -159,11 +155,6 @@ const headers = [
159155
value: "role",
160156
sortable: false,
161157
},
162-
{
163-
text: "Status",
164-
value: "status",
165-
sortable: false,
166-
},
167158
{
168159
text: "Actions",
169160
value: "actions",

ui/src/interfaces/IInvitation.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { BasicRole } from "@/interfaces/INamespace";
2+
3+
export interface IInvitation {
4+
status: "pending" | "expired";
5+
role: BasicRole;
6+
invited_by: string;
7+
expires_at: string;
8+
created_at: string;
9+
updated_at: string;
10+
status_updated_at: string;
11+
namespace: {
12+
tenant_id: string;
13+
name: string;
14+
};
15+
user: {
16+
id: string;
17+
email: string;
18+
}
19+
}
20+
21+
export interface IInviteMemberPayload {
22+
email: string;
23+
role: BasicRole;
24+
tenant_id: string;
25+
}

ui/src/interfaces/INamespace.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ export interface INamespaceMember {
55
id: string;
66
role: Role;
77
email: string;
8-
status: string;
98
added_at: string;
109
expires_at: string;
1110
}
@@ -40,30 +39,19 @@ export interface INamespace {
4039
type: NamespaceType;
4140
}
4241

43-
export interface INamespaceAcceptInvite {
44-
tenant: string;
45-
sig: string;
46-
}
47-
48-
export interface INamespaceAddMember {
49-
email: string;
50-
role: Role;
42+
export interface INamespaceEdit {
5143
tenant_id: string;
44+
name?: string;
45+
settings?: Partial<INamespaceSettings>;
5246
}
5347

5448
export interface INamespaceEditMember {
5549
user_id: string;
56-
role: Role;
50+
role: BasicRole;
5751
tenant_id: string;
5852
}
5953

6054
export interface INamespaceRemoveMember {
6155
tenant_id: string;
6256
user_id: string;
6357
}
64-
65-
export interface INamespaceEdit {
66-
tenant_id: string;
67-
name?: string;
68-
settings?: Partial<INamespaceSettings>;
69-
}

ui/src/store/api/invitations.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { namespacesApi } from "@/api/http";
2+
import { IInviteMemberPayload } from "@/interfaces/IInvitation";
3+
4+
export const fetchUserPendingInvitations = async (data: { filter: string, page: number, perPage: number }) =>
5+
namespacesApi.getMembershipInvitationList(data.filter, data.page, data.perPage);
6+
7+
export const fetchNamespaceInvitations = async (tenantId: string, data: { filter?: string, page: number, perPage: number }) =>
8+
namespacesApi.getNamespaceMembershipInvitationList(tenantId, data.filter, data.page, data.perPage);
9+
10+
export const declineNamespaceInvitation = async (tenant: string) =>
11+
namespacesApi.declineInvite(tenant);
12+
13+
export const acceptNamespaceInvitation = async (tenant: string) =>
14+
namespacesApi.acceptInvite(tenant);
15+
16+
export const sendNamespaceInvitationEmail = async (data: IInviteMemberPayload) => namespacesApi.addNamespaceMember(data.tenant_id, {
17+
email: data.email,
18+
role: data.role,
19+
});
20+
21+
export const generateNamespaceInvitationLink = async (data: IInviteMemberPayload) => namespacesApi.generateInvitationLink(data.tenant_id, {
22+
email: data.email,
23+
role: data.role,
24+
});

ui/src/store/api/namespaces.ts

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
import {
2-
INamespaceAcceptInvite,
3-
INamespaceAddMember,
4-
INamespaceEdit,
5-
INamespaceEditMember,
6-
INamespaceRemoveMember,
7-
} from "@/interfaces/INamespace";
1+
import { INamespaceEdit, INamespaceEditMember, INamespaceRemoveMember } from "@/interfaces/INamespace";
82
import { namespacesApi } from "@/api/http";
93

104
export const createNamespace = async (name: string) => namespacesApi.createNamespace({ name });
@@ -25,16 +19,6 @@ export const editNamespace = async (data: INamespaceEdit) => namespacesApi.editN
2519
},
2620
});
2721

28-
export const sendNamespaceLink = async (data: INamespaceAddMember) => namespacesApi.addNamespaceMember(data.tenant_id, {
29-
email: data.email,
30-
role: data.role,
31-
});
32-
33-
export const generateNamespaceLink = async (data: INamespaceAddMember) => namespacesApi.generateInvitationLink(data.tenant_id, {
34-
email: data.email,
35-
role: data.role,
36-
});
37-
3822
export const updateNamespaceMember = async (data: INamespaceEditMember) => namespacesApi.updateNamespaceMember(
3923
data.tenant_id,
4024
data.user_id,
@@ -48,8 +32,6 @@ export const removeUserFromNamespace = async (data: INamespaceRemoveMember) => n
4832

4933
export const switchNamespace = async (tenantId: string) => namespacesApi.getNamespaceToken(tenantId);
5034

51-
export const acceptNamespaceInvite = async (data: INamespaceAcceptInvite) => namespacesApi.acceptInvite(data.tenant, { sig: data.sig });
52-
5335
export const getSupportID = async (tenant: string) => namespacesApi.getNamespaceSupport(tenant);
5436

5537
export const lookupUserStatus = async (
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { ref } from "vue";
2+
import { defineStore } from "pinia";
3+
import { IInvitation, IInviteMemberPayload } from "@/interfaces/IInvitation";
4+
import * as invitationsApi from "../api/invitations";
5+
6+
const useInvitationsStore = defineStore("invitations", () => {
7+
const pendingInvitations = ref<IInvitation[]>([]);
8+
const namespaceInvitations = ref<IInvitation[]>([]);
9+
10+
const pendingInvitesFilter = JSON.stringify([{ type: "property", params: { name: "status", operator: "eq", value: "pending" } }]);
11+
const encodedFilter = Buffer.from(pendingInvitesFilter).toString("base64");
12+
13+
const fetchUserPendingInvitationList = async (perPage: number) => {
14+
const res = await invitationsApi.fetchUserPendingInvitations({
15+
filter: encodedFilter,
16+
page: 1,
17+
perPage,
18+
});
19+
pendingInvitations.value = res.data as IInvitation[];
20+
};
21+
22+
const fetchNamespaceInvitationList = async (tenantId: string, page: number, perPage: number) => {
23+
const res = await invitationsApi.fetchNamespaceInvitations(tenantId, {
24+
filter: undefined,
25+
page,
26+
perPage,
27+
});
28+
namespaceInvitations.value = res.data as IInvitation[];
29+
};
30+
31+
const acceptInvitation = async (tenant: string) => {
32+
await invitationsApi.acceptNamespaceInvitation(tenant);
33+
};
34+
35+
const declineInvitation = async (tenant: string) => {
36+
await invitationsApi.declineNamespaceInvitation(tenant);
37+
};
38+
39+
const sendInvitationEmail = async (data: IInviteMemberPayload) => {
40+
await invitationsApi.sendNamespaceInvitationEmail(data);
41+
};
42+
43+
const generateInvitationLink = async (data: IInviteMemberPayload) => {
44+
const response = await invitationsApi.generateNamespaceInvitationLink(data);
45+
return response.data.link as string;
46+
};
47+
48+
return {
49+
pendingInvitations,
50+
namespaceInvitations,
51+
fetchUserPendingInvitationList,
52+
fetchNamespaceInvitationList,
53+
acceptInvitation,
54+
declineInvitation,
55+
sendInvitationEmail,
56+
generateInvitationLink,
57+
};
58+
});
59+
60+
export default useInvitationsStore;

ui/src/store/modules/namespaces.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { ref } from "vue";
33
import * as namespacesApi from "../api/namespaces";
44
import {
55
INamespace,
6-
INamespaceAcceptInvite,
7-
INamespaceAddMember,
86
INamespaceEdit,
97
INamespaceEditMember,
108
INamespaceRemoveMember,
@@ -52,15 +50,6 @@ const useNamespacesStore = defineStore("namespaces", () => {
5250
}
5351
};
5452

55-
const sendEmailInvitation = async (data: INamespaceAddMember) => {
56-
await namespacesApi.sendNamespaceLink(data);
57-
};
58-
59-
const generateInvitationLink = async (data: INamespaceAddMember) => {
60-
const res = await namespacesApi.generateNamespaceLink(data);
61-
return res.data.link as string;
62-
};
63-
6453
const updateNamespaceMember = async (data: INamespaceEditMember) => {
6554
await namespacesApi.updateNamespaceMember(data);
6655
};
@@ -69,10 +58,6 @@ const useNamespacesStore = defineStore("namespaces", () => {
6958
await namespacesApi.removeUserFromNamespace(data);
7059
};
7160

72-
const acceptInvite = async (data: INamespaceAcceptInvite) => {
73-
await namespacesApi.acceptNamespaceInvite(data);
74-
};
75-
7661
const lookupUserStatus = async (data: { tenant: string; id: string; sig: string; }) => {
7762
const res = await namespacesApi.lookupUserStatus(data);
7863
userStatus.value = res.data.status;
@@ -106,11 +91,8 @@ const useNamespacesStore = defineStore("namespaces", () => {
10691
editNamespace,
10792
deleteNamespace,
10893
leaveNamespace,
109-
sendEmailInvitation,
110-
generateInvitationLink,
11194
updateNamespaceMember,
11295
removeMemberFromNamespace,
113-
acceptInvite,
11496
lookupUserStatus,
11597
switchNamespace,
11698
reset,

ui/src/views/NamespaceInviteCard.vue

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,17 @@
2525
color="error"
2626
data-test="decline-btn"
2727
:text="isUserValid ? 'Decline Invitation' : 'Back to Home Page'"
28-
@click="close()"
28+
@click="declineInvite()"
2929
/>
3030
<v-spacer data-test="spacer" />
3131
<v-btn
3232
variant="text"
3333
color="primary"
3434
data-test="accept-btn"
35+
text="Accept Invitation"
3536
:disabled="!isUserValid"
3637
@click="acceptInvite()"
37-
>
38-
Accept Invitation
39-
</v-btn>
38+
/>
4039
</v-card-actions>
4140
</template>
4241

@@ -45,10 +44,10 @@ import { computed, onMounted, ref } from "vue";
4544
import axios from "axios";
4645
import { useRouter, useRoute } from "vue-router";
4746
import useAuthStore from "@/store/modules/auth";
48-
import useNamespacesStore from "@/store/modules/namespaces";
47+
import useInvitationsStore from "@/store/modules/invitations";
4948
5049
const authStore = useAuthStore();
51-
const namespacesStore = useNamespacesStore();
50+
const invitationsStore = useInvitationsStore();
5251
const router = useRouter();
5352
const route = useRoute();
5453
@@ -85,27 +84,37 @@ const handleInviteError = (error: unknown) => {
8584
}
8685
};
8786
87+
const declineInvite = async () => {
88+
try {
89+
const tenant = (route.query["tenant-id"] || route.query.tenantid) as string;
90+
const sig = route.query.sig as string;
91+
92+
await invitationsStore.declineInvitation({ tenant, sig });
93+
94+
await close();
95+
} catch (error) {
96+
handleInviteError(error);
97+
}
98+
};
99+
88100
const acceptInvite = async () => {
89101
try {
90102
const tenant = (route.query["tenant-id"] || route.query.tenantid) as string;
91103
const sig = route.query.sig as string;
92104
93-
await namespacesStore.acceptInvite({ tenant, sig });
105+
await invitationsStore.acceptInvitation({ tenant, sig });
94106
95107
message.value = "Your invitation has been successfully accepted! You are now a member of the namespace.";
96108
97109
await authStore.enterInvitedNamespace(tenant);
98-
await namespacesStore.fetchNamespaceList();
99110
await close();
100111
} catch (error) {
101112
handleInviteError(error);
102113
}
103114
};
104115
105116
onMounted(() => {
106-
if (isUserValid.value) {
107-
return;
108-
}
117+
if (isUserValid.value) return;
109118
errorAlert.value = "You aren't logged in the account meant for this invitation.";
110119
});
111120

0 commit comments

Comments
 (0)