mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-11 00:08:37 +01:00
213 lines
7.3 KiB
Vue
213 lines
7.3 KiB
Vue
<script setup lang="ts">
|
|
import { DialogPermissionManager } from '#components';
|
|
import type { TPermission } from '~/shared/schema/TeamMemberSchema';
|
|
|
|
const { projectId, isGuest } = useProject();
|
|
|
|
definePageMeta({ layout: 'dashboard' });
|
|
|
|
const columns = [
|
|
{ key: 'me', label: '' },
|
|
{ key: 'email', label: 'Email' },
|
|
{ key: 'permission', label: 'Permission' },
|
|
{ key: 'pending', label: 'Status' },
|
|
{ key: 'action', label: 'Actions' },
|
|
]
|
|
|
|
const { data: members, refresh: refreshMembers } = useFetch('/api/project/members/list', {
|
|
headers: useComputedHeaders({ useSnapshotDates: false })
|
|
});
|
|
|
|
const showAddMember = ref<boolean>(false);
|
|
|
|
const addMemberEmail = ref<string>("");
|
|
|
|
const { createErrorAlert } = useAlert();
|
|
|
|
async function kickMember(email: string) {
|
|
const sure = confirm('Are you sure to kick ' + email + ' ?');
|
|
if (!sure) return;
|
|
try {
|
|
await $fetch('/api/project/members/kick', {
|
|
method: 'POST',
|
|
...signHeaders({
|
|
'Content-Type': 'application/json',
|
|
'x-pid': projectId.value ?? ''
|
|
}),
|
|
body: JSON.stringify({ email }),
|
|
onResponseError({ request, response, options }) {
|
|
createErrorAlert('Error', response.statusText);
|
|
}
|
|
});
|
|
|
|
refreshMembers();
|
|
} catch (ex: any) { }
|
|
}
|
|
|
|
async function addMember() {
|
|
|
|
if (addMemberEmail.value.length === 0) return;
|
|
|
|
try {
|
|
|
|
showAddMember.value = false;
|
|
|
|
await $fetch('/api/project/members/add', {
|
|
method: 'POST',
|
|
...signHeaders({
|
|
'Content-Type': 'application/json',
|
|
'x-pid': projectId.value ?? ''
|
|
}),
|
|
body: JSON.stringify({ email: addMemberEmail.value }),
|
|
onResponseError({ request, response, options }) {
|
|
createErrorAlert('Error', response.statusText);
|
|
}
|
|
});
|
|
|
|
addMemberEmail.value = '';
|
|
|
|
refreshMembers();
|
|
|
|
} catch (ex: any) { }
|
|
|
|
|
|
}
|
|
|
|
|
|
const modal = useModal();
|
|
|
|
function openPermissionManagerDialog(member_id: string) {
|
|
modal.open(DialogPermissionManager, {
|
|
preventClose: true,
|
|
member_id,
|
|
onSuccess: () => {
|
|
modal.close();
|
|
refreshMembers();
|
|
},
|
|
onCancel: () => {
|
|
modal.close();
|
|
refreshMembers();
|
|
},
|
|
});
|
|
}
|
|
|
|
function permissionToString(permission: TPermission) {
|
|
const result: string[] = [];
|
|
if (permission.webAnalytics) result.push('w');
|
|
if (permission.events) result.push('e');
|
|
if (permission.ai) result.push('a');
|
|
if (permission.domains.includes('All domains')) {
|
|
result.push('+');
|
|
} else {
|
|
result.push(permission.domains.length.toString());
|
|
}
|
|
return result.join('');
|
|
}
|
|
|
|
async function leaveProject() {
|
|
try {
|
|
await $fetch('/api/project/members/leave', {
|
|
headers: useComputedHeaders({}).value
|
|
});
|
|
location.reload();
|
|
} catch (ex: any) {
|
|
alert(ex.message);
|
|
}
|
|
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="p-6 pt-10">
|
|
|
|
<div v-if="!isGuest" class="flex flex-col gap-8">
|
|
|
|
<div class="flex flex-col">
|
|
<div class="flex gap-4 items-center">
|
|
<LyxUiInput class="px-4 py-1 w-full" placeholder="Add a new member" v-model="addMemberEmail">
|
|
</LyxUiInput>
|
|
<LyxUiButton @click="addMember" type="secondary"> Add </LyxUiButton>
|
|
</div>
|
|
<div class="poppins text-[.8rem] mt-2 dark:text-lyx-text-dark">
|
|
We will send an invitation email to the user you wish to add to this project.
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<UTable :rows="members || []" :columns="columns">
|
|
|
|
<template #me-data="e">
|
|
<i v-if="e.row.me" class="far fa-user text-lyx-lightmode-text dark:text-lyx-text"></i>
|
|
<i v-if="!e.row.me"></i>
|
|
</template>
|
|
|
|
<template #email-data="e">
|
|
<div class="text-lyx-lightmode-text dark:text-lyx-text">
|
|
{{ e.row.email }}
|
|
</div>
|
|
</template>
|
|
|
|
<template #pending-data="e">
|
|
<div class="text-lyx-lightmode-text dark:text-lyx-text">
|
|
{{ e.row.pending ? 'Pending' : 'Accepted' }}
|
|
</div>
|
|
</template>
|
|
|
|
<template #permission-data="e">
|
|
<div class="text-lyx-lightmode-text dark:text-lyx-text flex gap-2">
|
|
<div v-if="e.row.role !== 'OWNER' && !isGuest">
|
|
<LyxUiButton class="!px-2" type="secondary"
|
|
@click="openPermissionManagerDialog(e.row.id.toString())">
|
|
<UTooltip text="Manage permissions">
|
|
<i class="far fa-gear"></i>
|
|
</UTooltip>
|
|
</LyxUiButton>
|
|
</div>
|
|
|
|
<div class="flex gap-2 flex-wrap">
|
|
<UBadge variant="outline" size="sm" color="yellow"
|
|
v-if="!e.row.permission.webAnalytics && !e.row.permission.events && !e.row.permission.ai && e.row.permission.domains.length == 0">
|
|
No permission given
|
|
</UBadge>
|
|
<UBadge variant="outline" size="sm" v-if="e.row.permission.webAnalytics"
|
|
label="Analytics"> </UBadge>
|
|
<UBadge variant="outline" size="sm" v-if="e.row.permission.events" label="Events">
|
|
</UBadge>
|
|
<UBadge variant="outline" size="sm" v-if="e.row.permission.ai" label="AI"> </UBadge>
|
|
<UBadge variant="outline" color="blue" size="sm"
|
|
v-if="e.row.permission.domains.includes('All domains')" label="All domains">
|
|
</UBadge>
|
|
|
|
<UBadge variant="outline" size="sm" color="blue"
|
|
v-if="!e.row.permission.domains.includes('All domains')"
|
|
v-for="domain of e.row.permission.domains" :label="domain"> </UBadge>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
|
|
<template #action-data="e" v-if="!isGuest">
|
|
<div @click="kickMember(e.row.email)" v-if="e.row.role != 'OWNER'"
|
|
class="text-red-500 hover:bg-black/20 cursor-pointer outline outline-[1px] outline-red-500 px-3 py-1 rounded-lg text-center">
|
|
Remove
|
|
</div>
|
|
</template>
|
|
|
|
</UTable>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="isGuest" class="flex flex-col gap-8 mt-[10vh]">
|
|
<div class="flex flex-col gap-4 items-center">
|
|
<div class="text-[1.2rem]"> Leave this project </div>
|
|
<LyxUiButton @click="leaveProject()" type="primary"> Leave </LyxUiButton>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</template>
|