Skip to content

Commit eee1389

Browse files
Onatcerkorridor
authored andcommitted
add frontend to deactivate user
1 parent ac6e2b8 commit eee1389

File tree

6 files changed

+102
-3
lines changed

6 files changed

+102
-3
lines changed

app/Http/Controllers/Api/V1/MemberController.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ public function destroy(Organization $organization, Member $member, MemberServic
114114
* Make a member a placeholder member
115115
*
116116
* @throws AuthorizationException|CanNotRemoveOwnerFromOrganization|ChangingRoleOfPlaceholderIsNotAllowed
117+
*
118+
* @operationId makePlaceholder
117119
*/
118120
public function makePlaceholder(Organization $organization, Member $member, MemberService $memberService): JsonResponse
119121
{
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<script setup lang="ts">
2+
import SecondaryButton from '@/packages/ui/src/Buttons/SecondaryButton.vue';
3+
import DialogModal from '@/packages/ui/src/DialogModal.vue';
4+
import {ref} from 'vue';
5+
import {api, type Member} from '@/packages/api/src';
6+
import PrimaryButton from '@/packages/ui/src/Buttons/PrimaryButton.vue';
7+
import {useMutation} from '@tanstack/vue-query';
8+
import {getCurrentOrganizationId} from "@/utils/useUser";
9+
import {useNotificationsStore} from "@/utils/notification";
10+
import {useMembersStore} from "@/utils/useMembers";
11+
12+
const {handleApiRequestNotifications} = useNotificationsStore();
13+
14+
const show = defineModel('show', {default: false});
15+
const saving = ref(false);
16+
17+
const props = defineProps<{
18+
member: Member;
19+
}>();
20+
21+
const turnToPlaceholderMutation = useMutation({
22+
mutationFn: async () => {
23+
const organizationId = getCurrentOrganizationId();
24+
if (organizationId === null) {
25+
throw new Error('No current organization id - create report');
26+
}
27+
return await api.makePlaceholder(undefined, {
28+
params: {
29+
organization: organizationId,
30+
member: props.member.id
31+
},
32+
});
33+
},
34+
});
35+
36+
async function submit() {
37+
saving.value = true;
38+
await handleApiRequestNotifications(
39+
() =>
40+
turnToPlaceholderMutation.mutateAsync(),
41+
'Deactivating the member was successful!',
42+
'There was an error deactivating the user.',
43+
() => {
44+
show.value = false;
45+
useMembersStore().fetchMembers()
46+
}
47+
);
48+
}
49+
50+
</script>
51+
52+
<template>
53+
<DialogModal closeable :show="show" @close="show = false">
54+
<template #title>
55+
<div class="flex space-x-2">
56+
<span> Deactivate User </span>
57+
</div>
58+
</template>
59+
60+
<template #content>
61+
<p>
62+
Deactivating the user <strong>{{ member.name }} </strong> will remove the user's access to
63+
the organization. You will not be billed for inactive users and all time entries will be preserved.
64+
</p>
65+
</template>
66+
<template #footer>
67+
<SecondaryButton @click="show = false"> Cancel</SecondaryButton>
68+
69+
<PrimaryButton
70+
class="ms-3"
71+
:class="{ 'opacity-25': saving }"
72+
:disabled="saving"
73+
@click="submit()">
74+
Deactivate
75+
</PrimaryButton>
76+
</template>
77+
</DialogModal>
78+
</template>
79+
80+
<style scoped></style>

resources/js/Components/Common/Member/MemberMoreOptionsDropdown.vue

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
<script setup lang="ts">
2-
import { TrashIcon, PencilSquareIcon, ArrowDownOnSquareStackIcon } from '@heroicons/vue/20/solid';
2+
import { TrashIcon, UserCircleIcon, PencilSquareIcon, ArrowDownOnSquareStackIcon } from '@heroicons/vue/20/solid';
33
import type { Member } from '@/packages/api/src';
4-
import {canDeleteMembers, canMergeMembers, canUpdateMembers} from '@/utils/permissions';
4+
import {canDeleteMembers, canMakeMembersPlaceholders, canMergeMembers, canUpdateMembers} from '@/utils/permissions';
55
import MoreOptionsDropdown from '@/packages/ui/src/MoreOptionsDropdown.vue';
66
77
const emit = defineEmits<{
88
delete: [];
99
edit: [];
1010
merge: [];
11+
makePlaceholder: [];
1112
}>();
1213
const props = defineProps<{
1314
member: Member;
@@ -47,6 +48,14 @@ const props = defineProps<{
4748
<ArrowDownOnSquareStackIcon class="w-5 text-icon-active"></ArrowDownOnSquareStackIcon>
4849
<span>Merge</span>
4950
</button>
51+
<button
52+
v-if="props.member.role !== 'placeholder' && canMakeMembersPlaceholders()"
53+
:aria-label="'Make Member ' + props.member.name + ' a placeholder'"
54+
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
55+
@click="emit('makePlaceholder')">
56+
<UserCircleIcon class="w-5 text-icon-active"></UserCircleIcon>
57+
<span>Deactivate</span>
58+
</button>
5059
</div>
5160
</MoreOptionsDropdown>
5261
</template>

resources/js/Components/Common/Member/MemberTableRow.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ import MemberEditModal from '@/Components/Common/Member/MemberEditModal.vue';
1515
import { getOrganizationCurrencyString } from '@/utils/money';
1616
import { formatCents } from '@/packages/ui/src/utils/money';
1717
import MemberMergeModal from "@/Components/Common/Member/MemberMergeModal.vue";
18+
import MemberMakePlaceholderModal from "@/Components/Common/Member/MemberMakePlaceholderModal.vue";
1819
1920
const props = defineProps<{
2021
member: Member;
2122
}>();
2223
2324
const showEditMemberModal = ref(false);
2425
const showMergeMemberModal = ref(false);
26+
const showMakeMemberPlaceholderModal = ref(false);
2527
2628
function removeMember() {
2729
useMembersStore().removeMember(props.member.id);
@@ -106,12 +108,14 @@ const userHasValidMailAddress = computed(() => {
106108
@edit="showEditMemberModal = true"
107109
@delete="removeMember"
108110
@merge="showMergeMemberModal = true"
111+
@make-placeholder="showMakeMemberPlaceholderModal = true"
109112
></MemberMoreOptionsDropdown>
110113
</div>
111114
<MemberEditModal
112115
v-model:show="showEditMemberModal"
113116
:member="member"></MemberEditModal>
114117
<MemberMergeModal v-model:show="showMergeMemberModal" :member="member"></MemberMergeModal>
118+
<MemberMakePlaceholderModal v-model:show="showMakeMemberPlaceholderModal" :member="member"></MemberMakePlaceholderModal>
115119
</TableRow>
116120
</template>
117121

resources/js/packages/api/src/openapi.json.client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1472,7 +1472,7 @@ const endpoints = makeApi([
14721472
{
14731473
method: 'post',
14741474
path: '/v1/organizations/:organization/members/:member/make-placeholder',
1475-
alias: 'v1.members.make-placeholder',
1475+
alias: 'makePlaceholder',
14761476
requestFormat: 'json',
14771477
parameters: [
14781478
{

resources/js/utils/permissions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ export function canMergeMembers() {
8181
return currentUserHasPermission('members:merge-into');
8282
}
8383

84+
export function canMakeMembersPlaceholders() {
85+
return currentUserHasPermission('members:make-placeholder');
86+
}
87+
8488
export function canInvitePlaceholderMembers() {
8589
return currentUserHasPermission('members:invite-placeholder');
8690
}

0 commit comments

Comments
 (0)