Skip to content

Commit 8c1904a

Browse files
committed
feat(ui): add new Invitations view, table and components
1 parent a892b84 commit 8c1904a

File tree

14 files changed

+682
-22
lines changed

14 files changed

+682
-22
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<template>
2+
<div>
3+
<v-list-item
4+
:disabled="!hasAuthorization"
5+
data-test="invitation-cancel-btn"
6+
@click="showDialog = true"
7+
>
8+
<div class="d-flex align-center ga-2">
9+
<v-icon icon="mdi-close-circle" />
10+
<v-list-item-title data-test="invitation-cancel-title">Cancel</v-list-item-title>
11+
</div>
12+
</v-list-item>
13+
14+
<MessageDialog
15+
v-model="showDialog"
16+
title="Cancel Invitation"
17+
:description="`Are you sure you want to cancel the invitation for ${invitation.user.email}?`"
18+
icon="mdi-alert"
19+
icon-color="error"
20+
confirm-text="Cancel Invitation"
21+
confirm-color="error"
22+
:confirm-loading="isLoading"
23+
cancel-text="Close"
24+
confirm-data-test="cancel-invitation-btn"
25+
cancel-data-test="close-btn"
26+
data-test="invitation-cancel-dialog"
27+
@close="close"
28+
@confirm="cancelInvitation"
29+
@cancel="close"
30+
/>
31+
</div>
32+
</template>
33+
34+
<script setup lang="ts">
35+
import { ref } from "vue";
36+
import axios from "axios";
37+
import MessageDialog from "@/components/Dialogs/MessageDialog.vue";
38+
import { IInvitation } from "@/interfaces/IInvitation";
39+
import handleError from "@/utils/handleError";
40+
import useSnackbar from "@/helpers/snackbar";
41+
import useInvitationsStore from "@/store/modules/invitations";
42+
43+
const { invitation, hasAuthorization } = defineProps<{
44+
invitation: IInvitation;
45+
hasAuthorization: boolean;
46+
}>();
47+
48+
const emit = defineEmits(["update"]);
49+
const invitationsStore = useInvitationsStore();
50+
const snackbar = useSnackbar();
51+
const showDialog = ref(false);
52+
const isLoading = ref(false);
53+
54+
const close = () => { showDialog.value = false; };
55+
56+
const update = () => {
57+
emit("update");
58+
close();
59+
};
60+
61+
const handleCancelInvitationError = (error: unknown) => {
62+
if (axios.isAxiosError(error)) {
63+
const status = error.response?.status;
64+
switch (status) {
65+
case 400:
66+
snackbar.showError("Invalid invitation.");
67+
break;
68+
case 403:
69+
snackbar.showError("You don't have permission to cancel invitations.");
70+
break;
71+
case 404:
72+
snackbar.showError("Invitation not found.");
73+
break;
74+
default:
75+
snackbar.showError("Failed to cancel invitation.");
76+
}
77+
} else {
78+
snackbar.showError("Failed to cancel invitation.");
79+
}
80+
81+
handleError(error);
82+
};
83+
84+
const cancelInvitation = async () => {
85+
isLoading.value = true;
86+
try {
87+
await invitationsStore.cancelInvitation({
88+
tenant: invitation.namespace.tenant_id,
89+
user_id: invitation.user.id,
90+
});
91+
92+
snackbar.showSuccess("Successfully cancelled invitation.");
93+
update();
94+
} catch (error: unknown) {
95+
handleCancelInvitationError(error);
96+
} finally {
97+
isLoading.value = false;
98+
}
99+
};
100+
</script>
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<template>
2+
<div>
3+
<v-list-item
4+
:disabled="!hasAuthorization"
5+
data-test="invitation-edit-btn"
6+
@click="showDialog = true"
7+
>
8+
<div class="d-flex align-center ga-2">
9+
<v-icon icon="mdi-pencil" />
10+
<v-list-item-title data-test="invitation-edit-title">Edit Role</v-list-item-title>
11+
</div>
12+
</v-list-item>
13+
14+
<FormDialog
15+
v-model="showDialog"
16+
title="Update invitation role"
17+
icon="mdi-account-edit"
18+
confirm-text="Update"
19+
:confirm-loading="isLoading"
20+
cancel-text="Cancel"
21+
confirm-data-test="update-btn"
22+
cancel-data-test="cancel-btn"
23+
data-test="invitation-edit-dialog"
24+
@close="close"
25+
@confirm="editInvitation"
26+
@cancel="close"
27+
>
28+
<v-card-text class="pa-6">
29+
<RoleSelect v-model="newRole" />
30+
</v-card-text>
31+
</FormDialog>
32+
</div>
33+
</template>
34+
35+
<script setup lang="ts">
36+
import { ref } from "vue";
37+
import axios from "axios";
38+
import FormDialog from "@/components/Dialogs/FormDialog.vue";
39+
import { IInvitation } from "@/interfaces/IInvitation";
40+
import handleError from "@/utils/handleError";
41+
import useSnackbar from "@/helpers/snackbar";
42+
import RoleSelect from "@/components/Team/RoleSelect.vue";
43+
import useInvitationsStore from "@/store/modules/invitations";
44+
45+
const { invitation, hasAuthorization } = defineProps<{
46+
invitation: IInvitation;
47+
hasAuthorization: boolean;
48+
}>();
49+
50+
const emit = defineEmits(["update"]);
51+
const invitationsStore = useInvitationsStore();
52+
const snackbar = useSnackbar();
53+
const showDialog = ref(false);
54+
const isLoading = ref(false);
55+
const newRole = ref(invitation.role);
56+
57+
const close = () => {
58+
showDialog.value = false;
59+
newRole.value = invitation.role;
60+
};
61+
62+
const update = () => {
63+
emit("update");
64+
close();
65+
};
66+
67+
const handleEditInvitationError = (error: unknown) => {
68+
if (axios.isAxiosError(error)) {
69+
const status = error.response?.status;
70+
switch (status) {
71+
case 400:
72+
snackbar.showError("Invalid invitation or role.");
73+
break;
74+
case 403:
75+
snackbar.showError("You don't have permission to edit invitations.");
76+
break;
77+
case 404:
78+
snackbar.showError("Invitation not found.");
79+
break;
80+
default:
81+
snackbar.showError("Failed to update invitation role.");
82+
}
83+
} else {
84+
snackbar.showError("Failed to update invitation role.");
85+
}
86+
87+
handleError(error);
88+
};
89+
90+
const editInvitation = async () => {
91+
isLoading.value = true;
92+
try {
93+
await invitationsStore.editInvitation({
94+
tenant: invitation.namespace.tenant_id,
95+
user_id: invitation.user.id,
96+
role: newRole.value,
97+
});
98+
99+
snackbar.showSuccess("Successfully updated invitation role.");
100+
update();
101+
} catch (error: unknown) {
102+
handleEditInvitationError(error);
103+
} finally {
104+
isLoading.value = false;
105+
}
106+
};
107+
</script>

0 commit comments

Comments
 (0)