mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-14 11:12:16 +01:00
add frontend to deactivate user
This commit is contained in:
@@ -115,6 +115,8 @@ class MemberController extends Controller
|
|||||||
* Make a member a placeholder member
|
* Make a member a placeholder member
|
||||||
*
|
*
|
||||||
* @throws AuthorizationException|CanNotRemoveOwnerFromOrganization|ChangingRoleOfPlaceholderIsNotAllowed
|
* @throws AuthorizationException|CanNotRemoveOwnerFromOrganization|ChangingRoleOfPlaceholderIsNotAllowed
|
||||||
|
*
|
||||||
|
* @operationId makePlaceholder
|
||||||
*/
|
*/
|
||||||
public function makePlaceholder(Organization $organization, Member $member, MemberService $memberService): JsonResponse
|
public function makePlaceholder(Organization $organization, Member $member, MemberService $memberService): JsonResponse
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import SecondaryButton from '@/packages/ui/src/Buttons/SecondaryButton.vue';
|
||||||
|
import DialogModal from '@/packages/ui/src/DialogModal.vue';
|
||||||
|
import {ref} from 'vue';
|
||||||
|
import {api, type Member} from '@/packages/api/src';
|
||||||
|
import PrimaryButton from '@/packages/ui/src/Buttons/PrimaryButton.vue';
|
||||||
|
import {useMutation} from '@tanstack/vue-query';
|
||||||
|
import {getCurrentOrganizationId} from "@/utils/useUser";
|
||||||
|
import {useNotificationsStore} from "@/utils/notification";
|
||||||
|
import {useMembersStore} from "@/utils/useMembers";
|
||||||
|
|
||||||
|
const {handleApiRequestNotifications} = useNotificationsStore();
|
||||||
|
|
||||||
|
const show = defineModel('show', {default: false});
|
||||||
|
const saving = ref(false);
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
member: Member;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const turnToPlaceholderMutation = useMutation({
|
||||||
|
mutationFn: async () => {
|
||||||
|
const organizationId = getCurrentOrganizationId();
|
||||||
|
if (organizationId === null) {
|
||||||
|
throw new Error('No current organization id - create report');
|
||||||
|
}
|
||||||
|
return await api.makePlaceholder(undefined, {
|
||||||
|
params: {
|
||||||
|
organization: organizationId,
|
||||||
|
member: props.member.id
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
saving.value = true;
|
||||||
|
await handleApiRequestNotifications(
|
||||||
|
() =>
|
||||||
|
turnToPlaceholderMutation.mutateAsync(),
|
||||||
|
'Deactivating the member was successful!',
|
||||||
|
'There was an error deactivating the user.',
|
||||||
|
() => {
|
||||||
|
show.value = false;
|
||||||
|
useMembersStore().fetchMembers()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DialogModal closeable :show="show" @close="show = false">
|
||||||
|
<template #title>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<span> Deactivate User </span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
<p>
|
||||||
|
Deactivating the user <strong>{{ member.name }} </strong> will remove the user's access to
|
||||||
|
the organization. You will not be billed for inactive users and all time entries will be preserved.
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<SecondaryButton @click="show = false"> Cancel</SecondaryButton>
|
||||||
|
|
||||||
|
<PrimaryButton
|
||||||
|
class="ms-3"
|
||||||
|
:class="{ 'opacity-25': saving }"
|
||||||
|
:disabled="saving"
|
||||||
|
@click="submit()">
|
||||||
|
Deactivate
|
||||||
|
</PrimaryButton>
|
||||||
|
</template>
|
||||||
|
</DialogModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { TrashIcon, PencilSquareIcon, ArrowDownOnSquareStackIcon } from '@heroicons/vue/20/solid';
|
import { TrashIcon, UserCircleIcon, PencilSquareIcon, ArrowDownOnSquareStackIcon } from '@heroicons/vue/20/solid';
|
||||||
import type { Member } from '@/packages/api/src';
|
import type { Member } from '@/packages/api/src';
|
||||||
import {canDeleteMembers, canMergeMembers, canUpdateMembers} from '@/utils/permissions';
|
import {canDeleteMembers, canMakeMembersPlaceholders, canMergeMembers, canUpdateMembers} from '@/utils/permissions';
|
||||||
import MoreOptionsDropdown from '@/packages/ui/src/MoreOptionsDropdown.vue';
|
import MoreOptionsDropdown from '@/packages/ui/src/MoreOptionsDropdown.vue';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
edit: [];
|
edit: [];
|
||||||
merge: [];
|
merge: [];
|
||||||
|
makePlaceholder: [];
|
||||||
}>();
|
}>();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
member: Member;
|
member: Member;
|
||||||
@@ -47,6 +48,14 @@ const props = defineProps<{
|
|||||||
<ArrowDownOnSquareStackIcon class="w-5 text-icon-active"></ArrowDownOnSquareStackIcon>
|
<ArrowDownOnSquareStackIcon class="w-5 text-icon-active"></ArrowDownOnSquareStackIcon>
|
||||||
<span>Merge</span>
|
<span>Merge</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="props.member.role !== 'placeholder' && canMakeMembersPlaceholders()"
|
||||||
|
:aria-label="'Make Member ' + props.member.name + ' a placeholder'"
|
||||||
|
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"
|
||||||
|
@click="emit('makePlaceholder')">
|
||||||
|
<UserCircleIcon class="w-5 text-icon-active"></UserCircleIcon>
|
||||||
|
<span>Deactivate</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</MoreOptionsDropdown>
|
</MoreOptionsDropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import MemberEditModal from '@/Components/Common/Member/MemberEditModal.vue';
|
|||||||
import { getOrganizationCurrencyString } from '@/utils/money';
|
import { getOrganizationCurrencyString } from '@/utils/money';
|
||||||
import { formatCents } from '@/packages/ui/src/utils/money';
|
import { formatCents } from '@/packages/ui/src/utils/money';
|
||||||
import MemberMergeModal from "@/Components/Common/Member/MemberMergeModal.vue";
|
import MemberMergeModal from "@/Components/Common/Member/MemberMergeModal.vue";
|
||||||
|
import MemberMakePlaceholderModal from "@/Components/Common/Member/MemberMakePlaceholderModal.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
member: Member;
|
member: Member;
|
||||||
@@ -22,6 +23,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const showEditMemberModal = ref(false);
|
const showEditMemberModal = ref(false);
|
||||||
const showMergeMemberModal = ref(false);
|
const showMergeMemberModal = ref(false);
|
||||||
|
const showMakeMemberPlaceholderModal = ref(false);
|
||||||
|
|
||||||
function removeMember() {
|
function removeMember() {
|
||||||
useMembersStore().removeMember(props.member.id);
|
useMembersStore().removeMember(props.member.id);
|
||||||
@@ -106,12 +108,14 @@ const userHasValidMailAddress = computed(() => {
|
|||||||
@edit="showEditMemberModal = true"
|
@edit="showEditMemberModal = true"
|
||||||
@delete="removeMember"
|
@delete="removeMember"
|
||||||
@merge="showMergeMemberModal = true"
|
@merge="showMergeMemberModal = true"
|
||||||
|
@make-placeholder="showMakeMemberPlaceholderModal = true"
|
||||||
></MemberMoreOptionsDropdown>
|
></MemberMoreOptionsDropdown>
|
||||||
</div>
|
</div>
|
||||||
<MemberEditModal
|
<MemberEditModal
|
||||||
v-model:show="showEditMemberModal"
|
v-model:show="showEditMemberModal"
|
||||||
:member="member"></MemberEditModal>
|
:member="member"></MemberEditModal>
|
||||||
<MemberMergeModal v-model:show="showMergeMemberModal" :member="member"></MemberMergeModal>
|
<MemberMergeModal v-model:show="showMergeMemberModal" :member="member"></MemberMergeModal>
|
||||||
|
<MemberMakePlaceholderModal v-model:show="showMakeMemberPlaceholderModal" :member="member"></MemberMakePlaceholderModal>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1472,7 +1472,7 @@ const endpoints = makeApi([
|
|||||||
{
|
{
|
||||||
method: 'post',
|
method: 'post',
|
||||||
path: '/v1/organizations/:organization/members/:member/make-placeholder',
|
path: '/v1/organizations/:organization/members/:member/make-placeholder',
|
||||||
alias: 'v1.members.make-placeholder',
|
alias: 'makePlaceholder',
|
||||||
requestFormat: 'json',
|
requestFormat: 'json',
|
||||||
parameters: [
|
parameters: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ export function canMergeMembers() {
|
|||||||
return currentUserHasPermission('members:merge-into');
|
return currentUserHasPermission('members:merge-into');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function canMakeMembersPlaceholders() {
|
||||||
|
return currentUserHasPermission('members:make-placeholder');
|
||||||
|
}
|
||||||
|
|
||||||
export function canInvitePlaceholderMembers() {
|
export function canInvitePlaceholderMembers() {
|
||||||
return currentUserHasPermission('members:invite-placeholder');
|
return currentUserHasPermission('members:invite-placeholder');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user